PIVX Core  5.6.99
P2P Digital Currency
send.cpp
Go to the documentation of this file.
1 // Copyright (c) 2019-2022 The PIVX Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include "send.h"
6 #include "ui_send.h"
7 
8 #include "addnewcontactdialog.h"
9 #include "addresstablemodel.h"
10 #include "clientmodel.h"
11 #include "coincontrol.h"
12 #include "destination_io.h"
13 #include "guitransactionsutils.h"
14 #include "key_io.h"
15 #include "loadingdialog.h"
16 #include "openuridialog.h"
17 #include "operationresult.h"
18 #include "optionbutton.h"
19 #include "optionsmodel.h"
20 #include "qt/walletmodel.h"
21 #include "qtutils.h"
22 #include "sapling/address.h"
23 #include "sapling/key_io_sapling.h"
24 #include "script/standard.h"
26 #include "sendconfirmdialog.h"
27 
28 #define REQUEST_PREPARE_TX 1
29 #define REQUEST_REFRESH_BALANCE 2
30 
32  PWidget(parent),
33  ui(new Ui::send),
34  coinIcon(new QPushButton())
35 {
36  ui->setupUi(this);
37 
38  this->setStyleSheet(parent->styleSheet());
39 
40  /* Containers */
41  setCssProperty(ui->left, "container");
42  ui->left->setContentsMargins(0,20,0,20);
43  setCssProperty(ui->right, "container-right");
44  ui->right->setContentsMargins(20,10,20,20);
45 
46  /* Light Font */
47  QFont fontLight;
48  fontLight.setWeight(QFont::Light);
49 
50  /* Title */
51  setCssProperty(ui->labelTitle, "text-title-screen");
52  ui->labelTitle->setFont(fontLight);
53 
54  /* Button Group */
55  setCssProperty(ui->pushLeft, "btn-check-left");
56  ui->pushLeft->setChecked(true);
57  setCssProperty(ui->pushRight, "btn-check-right");
58 
59  /* Subtitle */
60  setCssProperty({ui->labelSubtitle1, ui->labelSubtitle2}, "text-subtitle");
61 
62  /* Address - Amount*/
63  setCssProperty({ui->labelSubtitleAddress, ui->labelSubtitleAmount}, "text-title");
64 
65  /* Buttons */
66  setCssBtnSecondary(ui->pushButtonFee);
67  setCssProperty(ui->pushButtonClear, "btn-secundary-clear");
68  setCssProperty(ui->pushButtonAddRecipient, "btn-secundary-add");
69  setCssBtnPrimary(ui->pushButtonSave);
70  setCssBtnSecondary(ui->pushButtonReset);
71 
72  // Coin control
73  ui->btnCoinControl->setTitleClassAndText("btn-title-grey", tr("Coin Control"));
74  ui->btnCoinControl->setSubTitleClassAndText("text-subtitle", tr("Select the source of the coins"));
75 
76  // Change address option
77  ui->btnChangeAddress->setTitleClassAndText("btn-title-grey", tr("Change Address"));
78  ui->btnChangeAddress->setSubTitleClassAndText("text-subtitle", tr("Customize the change address"));
79 
80  // Uri
81  ui->btnUri->setTitleClassAndText("btn-title-grey", tr("Open URI"));
82  ui->btnUri->setSubTitleClassAndText("text-subtitle", tr("Parse a PIVX URI"));
83 
84  // Shield coins
85  ui->btnShieldCoins->setTitleClassAndText("btn-title-grey", tr("Shield Coins"));
86  ui->btnShieldCoins->setSubTitleClassAndText("text-subtitle", tr("Convert all transparent coins into shielded coins"));
87  ui->btnShieldCoins->setVisible(false);
88 
89  connect(ui->pushButtonFee, &QPushButton::clicked, this, &SendWidget::onChangeCustomFeeClicked);
90  connect(ui->btnCoinControl, &OptionButton::clicked, this, &SendWidget::onCoinControlClicked);
91  connect(ui->btnChangeAddress, &OptionButton::clicked, this, &SendWidget::onChangeAddressClicked);
92  connect(ui->btnUri, &OptionButton::clicked, this, &SendWidget::onOpenUriClicked);
93  connect(ui->btnShieldCoins, &OptionButton::clicked, this, &SendWidget::onShieldCoinsClicked);
94  connect(ui->pushButtonReset, &QPushButton::clicked, [this](){ onResetCustomOptions(true); });
95  connect(ui->checkBoxDelegations, &QCheckBox::stateChanged, this, &SendWidget::onCheckBoxChanged);
96 
97  setCssProperty(ui->coinWidget, "container-coin-type");
98  setCssProperty(ui->labelLine, "container-divider");
99 
100 
101  // Total Send
102  setCssProperty(ui->labelTitleTotalSend, "text-title");
103  setCssProperty(ui->labelAmountSend, "text-body1");
104 
105  // Total Remaining
106  setCssProperty(ui->labelTitleTotalRemaining, "text-title");
107  setCssProperty(ui->labelAmountRemaining, "text-body1");
108 
109  // Icon Send
110  ui->stackedWidget->addWidget(coinIcon);
111  coinIcon->show();
112  coinIcon->raise();
113 
114  setCssProperty(coinIcon, "coin-icon-piv");
115 
116  QSize BUTTON_SIZE = QSize(24, 24);
117  coinIcon->setMinimumSize(BUTTON_SIZE);
118  coinIcon->setMaximumSize(BUTTON_SIZE);
119 
120  int posX = 0;
121  int posY = 20;
122  coinIcon->move(posX, posY);
123 
124  // Entry
125  addEntry();
126 
127  // Init custom fee false (updated in loadWalletModel)
128  setCustomFeeSelected(false);
129 
130  // Connect
131  connect(ui->pushLeft, &QPushButton::clicked, [this](){onPIVSelected(true);});
132  connect(ui->pushRight, &QPushButton::clicked, [this](){onPIVSelected(false);});
133  connect(ui->pushButtonSave, &QPushButton::clicked, this, &SendWidget::onSendClicked);
134  connect(ui->pushButtonAddRecipient, &QPushButton::clicked, this, &SendWidget::onAddEntryClicked);
135  connect(ui->pushButtonClear, &QPushButton::clicked, [this](){clearAll(true);});
136 
138 }
139 
141 {
142  CAmount total = 0;
143  QMutableListIterator<SendMultiRow*> it(entries);
144  while (it.hasNext()) {
145  SendMultiRow* entry = it.next();
146  CAmount amount = entry->getAmountValue();
147  if (amount > 0)
148  total += amount;
149  }
150 
152 
153  CAmount totalAmount = 0;
154  CAmount delegatedBalance = 0;
155  QString titleTotalRemaining;
157  // Set remaining balance to the sum of the coinControl selected inputs
158  std::vector<OutPointWrapper> coins;
160  CAmount selectedBalance = 0;
161  for (const auto& coin : coins) {
162  selectedBalance += coin.value;
163  }
164  totalAmount = selectedBalance - total;
165  titleTotalRemaining = tr("Total remaining from the selected UTXO");
166  } else {
168  if (isTransparent) {
169  totalAmount = balances.balance - balances.shielded_balance - walletModel->getLockedBalance(isTransparent) - total;
170  if (!fDelegationsChecked) {
171  totalAmount -= balances.delegate_balance;
172  }
173  // show delegated balance if exist
174  delegatedBalance = balances.delegate_balance;
175  } else {
176  totalAmount = balances.shielded_balance - total - walletModel->getLockedBalance(isTransparent);
177  }
178  titleTotalRemaining = tr("Unlocked remaining");
179  }
180 
181  QString type = isTransparent ? "transparent" : "shielded";
182  QString labelAmountRemaining = GUIUtil::formatBalance( totalAmount, nDisplayUnit, false) + " " + type;
183  QMetaObject::invokeMethod(this, "updateAmounts", Qt::QueuedConnection,
184  Q_ARG(QString, titleTotalRemaining),
185  Q_ARG(QString, GUIUtil::formatBalance(total, nDisplayUnit, false)),
186  Q_ARG(QString, labelAmountRemaining),
187  Q_ARG(CAmount, delegatedBalance));
188 }
189 
190 void SendWidget::updateAmounts(const QString& _titleTotalRemaining,
191  const QString& _labelAmountSend,
192  const QString& _labelAmountRemaining,
193  CAmount _delegationBalance)
194 {
195  ui->labelTitleTotalRemaining->setText(_titleTotalRemaining);
196  ui->labelAmountSend->setText(_labelAmountSend);
197  ui->labelAmountRemaining->setText(_labelAmountRemaining);
198  // show or hide delegations checkbox if need be
199  showHideCheckBoxDelegations(_delegationBalance);
200 }
201 
203 {
204  if (clientModel) {
205  connect(clientModel, &ClientModel::numBlocksChanged, [this](){
207  });
208  }
209 }
210 
212 {
213  if (walletModel) {
214  if (walletModel->getOptionsModel()) {
215  // display unit
217  }
218 
219  // set walletModel for entries
220  for (SendMultiRow *entry : entries) {
221  if (entry) {
222  entry->setWalletModel(walletModel);
223  }
224  }
225 
226  // Restore custom fee from wallet Settings
227  CAmount nCustomFee;
228  if (walletModel->getWalletCustomFee(nCustomFee)) {
229  setCustomFeeSelected(true, nCustomFee);
230  }
231 
232  // TODO: This only happen when the coin control features are modified in other screen, check before do this if the wallet has another screen modifying it.
233  // Coin Control
234  //connect(walletModel->getOptionsModel(), &OptionsModel::coinControlFeaturesChanged, [this](){});
235  //ui->frameCoinControl->setVisible(model->getOptionsModel()->getCoinControlFeatures());
236  //coinControlUpdateLabels();
237  }
238 }
239 
241 {
242  if (menuContacts && menuContacts->isVisible()) {
243  menuContacts->hide();
244  }
245 }
246 
247 void SendWidget::clearAll(bool fClearSettings)
248 {
249  onResetCustomOptions(false);
250  if (fClearSettings) onResetSettings();
252  clearEntries();
254 }
255 
257 {
259  setCustomFeeSelected(false);
260  if (walletModel) walletModel->setWalletCustomFee(false, DEFAULT_TRANSACTION_FEE);
261 }
262 
263 void SendWidget::onResetCustomOptions(bool fRefreshAmounts)
264 {
265  ui->btnChangeAddress->setActive(false);
266  if (ui->checkBoxDelegations->isChecked()) ui->checkBoxDelegations->setChecked(false);
268  if (fRefreshAmounts) {
270  }
271 }
272 
274 {
276  ui->btnCoinControl->setActive(false);
277 }
278 
280 {
281  if (coinControlDialog) {
284  }
285  ui->btnChangeAddress->setActive(false);
286 }
287 
289 {
290  int num = entries.length();
291  for (int i = 0; i < num; ++i) {
292  ui->scrollAreaWidgetContents->layout()->takeAt(0)->widget()->deleteLater();
293  }
294  entries.clear();
295 
296  addEntry();
297 }
298 
300 {
301  if (entries.isEmpty()) {
302  createEntry();
303  } else {
304  if (entries.length() == 1) {
305  SendMultiRow *entry = entries.at(0);
306  entry->hideLabels();
307  entry->setNumber(1);
308  } else if (entries.length() == MAX_SEND_POPUP_ENTRIES) {
309  inform(tr("Maximum amount of outputs reached"));
310  return;
311  }
312 
313  SendMultiRow *sendMultiRow = createEntry();
314  sendMultiRow->setNumber(entries.length());
315  sendMultiRow->hideLabels();
316  }
318 }
319 
321 {
322  SendMultiRow *sendMultiRow = new SendMultiRow(window, this);
323  if (this->walletModel) sendMultiRow->setWalletModel(this->walletModel);
324  entries.append(sendMultiRow);
325  ui->scrollAreaWidgetContents->layout()->addWidget(sendMultiRow);
326  connect(sendMultiRow, &SendMultiRow::onContactsClicked, this, &SendWidget::onContactsClicked);
327  connect(sendMultiRow, &SendMultiRow::onMenuClicked, this, &SendWidget::onMenuClicked);
328  connect(sendMultiRow, &SendMultiRow::onValueChanged, this, &SendWidget::onValueChanged);
329  return sendMultiRow;
330 }
331 
333 {
334  // Check prev valid entries before add a new one.
335  for (SendMultiRow* entry : entries) {
336  if (!entry || !entry->validate()) {
337  inform(tr("Invalid entry, previous entries must be valid before add a new one"));
338  return;
339  }
340  }
341  addEntry();
342 }
343 
344 void SendWidget::resizeEvent(QResizeEvent *event)
345 {
346  resizeMenu();
347  QWidget::resizeEvent(event);
348 }
349 
350 void SendWidget::showEvent(QShowEvent *event)
351 {
352  // Set focus on last recipient address when Send-window is displayed
355 }
356 
358 {
359  if (!entries.isEmpty()) entries.last()->setFocus();
360 }
361 
363 {
364  // Show checkbox only when there is any available owned delegation and
365  // coincontrol is not selected, and we are trying to spend transparent PIVs.
366  const bool isCControl = coinControlDialog ? coinControlDialog->coinControl->HasSelected() : false;
367  const bool hasDel = delegationBalance > 0;
368 
369  const bool showCheckBox = isTransparent && !isCControl && hasDel;
370  ui->checkBoxDelegations->setVisible(showCheckBox);
371  if (showCheckBox)
372  ui->checkBoxDelegations->setToolTip(
373  tr("Possibly spend coins delegated for cold-staking (currently available: %1").arg(
374  GUIUtil::formatBalance(delegationBalance, nDisplayUnit, false))
375  );
376 }
377 
379 {
381  return;
382 
383  QList<SendCoinsRecipient> recipients;
384  bool hasShieldedOutput = false;
385 
386  for (SendMultiRow* entry : entries) {
387  // TODO: Check UTXO splitter here..
388  // Validate send..
389  if (entry && entry->validate()) {
390  auto recipient = entry->getValue();
391  bool isShielded = recipient.isShieldedAddr;
392  if (!hasShieldedOutput) hasShieldedOutput = isShielded;
393  if (!recipient.message.isEmpty() && !isShielded) {
394  // memo set for transparent address
395  if (!ask(tr("Warning!"),
396  tr("Cannot send memo to address\n%1\n\n"
397  "Encrypted memo messages are available only for shielded recipients.\n\n"
398  "Do you wish to proceed without memo?\n").arg(recipient.address))) {
399  return;
400  } else {
401  // remove memo, so it doesn't show on the confirmation dialog.
402  recipient.message.clear();
403  }
404  }
405  recipients.append(recipient);
406  } else {
407  inform(tr("Invalid entry"));
408  return;
409  }
410  }
411 
412  if (recipients.isEmpty()) {
413  inform(tr("No set recipients"));
414  return;
415  }
416 
417  ProcessSend(recipients, hasShieldedOutput);
418 }
419 
420 void SendWidget::ProcessSend(QList<SendCoinsRecipient>& recipients, bool hasShieldedOutput,
421  const std::function<bool(QList<SendCoinsRecipient>&)>& func)
422 {
423  // First check SPORK_20 (before unlock)
424  bool isShieldedTx = hasShieldedOutput || !isTransparent || coinControlDialog->coinControl->destShieldChange;
425  if (isShieldedTx) {
427  inform(tr("Sapling Protocol temporarily in maintenance. Shielded transactions disabled (SPORK 20)"));
428  return;
429  }
430  }
431 
432  auto ptrUnlockedContext = std::make_unique<WalletModel::UnlockContext>(walletModel->requestUnlock());
433  if (!ptrUnlockedContext->isValid()) {
434  // Unlock wallet was cancelled
435  inform(tr("Cannot send, wallet locked"));
436  return;
437  }
438 
439  // Perform needed operation that requires the wallet unlocked
440  if (func && !func(recipients)) return;
441 
442  // If tx exists then there is an on-going process being executed, return.
443  if (isProcessing || ptrModelTx) {
444  inform(tr("On going process being executed, please wait until it's finished to create a new transaction"));
445  return;
446  }
447  ptrModelTx = new WalletModelTransaction(recipients);
448  ptrModelTx->useV2 = isShieldedTx;
449 
450  // Prepare tx
451  window->showHide(true);
452  LoadingDialog *dialog = new LoadingDialog(window, tr("Preparing transaction"));
453  dialog->execute(this, REQUEST_PREPARE_TX, std::move(ptrUnlockedContext));
455 
456  // If all went well, ask if want to broadcast it
457  if (processingResult) {
458  if (sendFinalStep()) {
460  }
461  } else if (!processingResultError->isEmpty()){
463  }
464 
465  // Process finished, can reset the tx model now. todo: this can get wrapped on a cached struct.
466  delete ptrModelTx;
467  ptrModelTx = nullptr;
468  if (processingResultError) {
469  processingResultError->clear();
470  processingResultError = nullopt;
471  }
472  processingResult = false;
473 }
474 
475 OperationResult SendWidget::prepareShielded(WalletModelTransaction* currentTransaction, bool fromTransparent)
476 {
477  bool hasCoinsOrNotesSelected = coinControlDialog && coinControlDialog->coinControl;
478  return walletModel->PrepareShieldedTransaction(currentTransaction,
479  fromTransparent,
480  hasCoinsOrNotesSelected ? coinControlDialog->coinControl : nullptr);
481 }
482 
484 {
485  if (!walletModel) return errorOut("Error, no wallet model loaded");
486  // prepare transaction for getting txFee earlier
487  WalletModel::SendCoinsReturn prepareStatus;
488  prepareStatus = walletModel->prepareTransaction(currentTransaction,
491 
492  // process prepareStatus and on error generate message shown to user
495  prepareStatus,
496  walletModel,
497  informType,
499  currentTransaction->getTransactionFee()),
500  true
501  );
502 
503  if (!informMsg.isEmpty()) {
504  return errorOut(informMsg.toStdString());
505  }
506 
507  if (prepareStatus.status != WalletModel::OK) {
508  return errorOut("Cannot create transaction.");
509  }
510  return OperationResult(true);
511 }
512 
514 {
515  showHideOp(true);
516  const bool fStakeDelegationVoided = ptrModelTx->fIsStakeDelegationVoided;
517  QString warningStr = QString();
518  if (fStakeDelegationVoided)
519  warningStr = tr("WARNING:\nTransaction spends a cold-stake delegation, voiding it.\n"
520  "These coins will no longer be cold-staked.");
521  TxDetailDialog* dialog = new TxDetailDialog(window, true, warningStr);
523  dialog->setData(walletModel, ptrModelTx);
524  dialog->adjustSize();
525  openDialogWithOpaqueBackgroundY(dialog, window, 3, 15);
526 
527  if (dialog->isConfirm()) {
528  // now send the prepared transaction
529  WalletModel::SendCoinsReturn sendStatus = dialog->getStatus();
530  // process sendStatus and on error generate message shown to user
532  this,
533  sendStatus,
535  );
536 
537  if (sendStatus.status == WalletModel::OK) {
538  clearAll(false);
539  inform(tr("Transaction sent"));
540  dialog->deleteLater();
541  return true;
542  }
543  }
544 
545  dialog->deleteLater();
546  return false;
547 }
548 
549 void SendWidget::run(int type)
550 {
551  if (type == REQUEST_PREPARE_TX) {
552  assert(!processingResult);
553  if (!isProcessing) {
554  isProcessing = true;
555  OperationResult result(false);
556  if ((result = ptrModelTx->useV2 ?
559  )) {
560  processingResult = true;
561  } else {
562  processingResult = false;
563  processingResultError = tr(result.getError().c_str());
564  }
565  isProcessing = false;
566  }
567  } else if (type == REQUEST_REFRESH_BALANCE) {
568  if (!isUpdatingBalance) {
569  isUpdatingBalance = true;
570  refreshAmounts();
571  isUpdatingBalance = false;
572  }
573  }
574 }
575 
576 void SendWidget::onError(QString error, int type)
577 {
578  isProcessing = false;
580 }
581 
583 {
585  inform(tr("Processing full, refreshing amounts later"));
586  }
587 }
588 
589 void SendWidget::updateEntryLabels(const QList<SendCoinsRecipient>& recipients)
590 {
591  for (const SendCoinsRecipient& rec : recipients) {
592  QString label = rec.label;
593  if (!label.isNull()) {
594  QString labelOld = walletModel->getAddressTableModel()->labelForAddress(rec.address);
595  if (label.compare(labelOld) != 0) {
596  CTxDestination dest = DecodeDestination(rec.address.toStdString());
597  if (!walletModel->updateAddressBookLabels(dest, label.toStdString(),
598  this->walletModel->isMine(dest) ?
601  // Label update failed
602  Q_EMIT message("", tr("Address label update failed for address: %1").arg(rec.address), CClientUIInterface::MSG_ERROR);
603  return;
604  }
605  }
606  }
607 
608  }
609 }
610 
612 {
613  showHideOp(true);
616  dialog->setAddress(QString::fromStdString(EncodeDestination(coinControlDialog->coinControl->destChange)));
619  }
620 
621  CWDestination destChange = (openDialogWithOpaqueBackgroundY(dialog, window, 3, 5) ?
622  dialog->getDestination() :
623  CNoDestination());
624 
625  if (!Standard::IsValidDestination(destChange)) {
626  // no change address set
627  ui->btnChangeAddress->setActive(false);
628  } else {
629  // Ask confirmation if external address
630  if (!walletModel->isMine(destChange) && !ask(tr("Warning!"),
631  tr("The change address doesn't belong to this wallet.\n\nDo you want to continue?"))) {
632  dialog->deleteLater();
633  return;
634  }
635  ui->btnChangeAddress->setActive(true);
636  }
637 
638  // save change address in coin control
639  const CTxDestination* transparentDest = Standard::GetTransparentDestination(destChange);
640  if (transparentDest) {
641  coinControlDialog->coinControl->destChange = *transparentDest;
643  }
645  if (shieldDest) {
648  }
649  dialog->deleteLater();
650 }
651 
653 {
654  showHideOp(true);
655  OpenURIDialog *dlg = new OpenURIDialog(window);
656  if (openDialogWithOpaqueBackgroundY(dlg, window, 3, 5)) {
657 
658  SendCoinsRecipient rcp;
659  if (!GUIUtil::parseBitcoinURI(dlg->getURI(), &rcp)) {
660  inform(tr("Invalid URI"));
661  return;
662  }
663  if (!walletModel->validateAddress(rcp.address)) {
664  inform(tr("Invalid address in URI"));
665  return;
666  }
667 
668  int listSize = entries.size();
669  if (listSize == 1) {
670  SendMultiRow *entry = entries[0];
672  entry->setAmount(BitcoinUnits::format(nDisplayUnit, rcp.amount, false));
673  } else {
674  // Use the last one if it's invalid or add a new one
675  SendMultiRow *entry = entries[listSize - 1];
676  if (!entry->validate()) {
677  addEntry();
678  entry = entries[listSize];
679  }
681  entry->setAmount(BitcoinUnits::format(nDisplayUnit, rcp.amount, false));
682  }
683  Q_EMIT receivedURI(dlg->getURI());
684  }
685  dlg->deleteLater();
686 }
687 
689 {
690  showHideOp(true);
691  if (!customFeeDialog) {
693  }
695  const CAmount& nFeePerKb = customFeeDialog->getFeeRate().GetFeePerK();
697  }
698 }
699 
701 {
702  if (walletModel->getBalance() > 0) {
703  // future: move coin control initialization and refresh to a worker thread.
708  coinControlDialog->exec();
709  ui->btnCoinControl->setActive(coinControlDialog->coinControl->HasSelected());
711  } else {
712  inform(tr("You don't have any %1 to select.").arg(CURRENCY_UNIT.c_str()));
713  }
714 }
715 
717 {
719  inform(tr("Sapling Protocol temporarily in maintenance. Shielded transactions disabled (SPORK 20)"));
720  return;
721  }
722 
723  auto balances = walletModel->GetWalletBalances();
724  CAmount availableBalance = balances.balance - balances.shielded_balance - walletModel->getLockedBalance(true);
725  if (availableBalance > 0) {
726 
727  // Calculate the required fee first. TODO future: Unify this code with the code in coincontroldialog into the model.
728  std::map<WalletModel::ListCoinsKey, std::vector<WalletModel::ListCoinsValue>> mapCoins;
729  walletModel->listCoins(mapCoins);
730  unsigned int nBytesInputs = 0;
731  for (const auto& out : mapCoins) {
732  bool isP2CS = out.first.stakerAddress != nullopt;
733  nBytesInputs += (CTXIN_SPEND_DUST_SIZE + (isP2CS ? 1 : 0)) * out.second.size();
734  }
735  nBytesInputs += OUTPUTDESCRIPTION_SIZE;
736  nBytesInputs += (BINDINGSIG_SIZE + 8);
737  // (plus at least 2 bytes for shielded in/outs len sizes)
738  nBytesInputs += 2;
739  // ExtraPayload size for special txes. For now 1 byte for nullopt.
740  nBytesInputs += 1;
741  // nVersion, nType, nLockTime and vin/vout len sizes
742  nBytesInputs += 10;
743  CAmount nPayFee = GetMinRelayFee(nBytesInputs) * DEFAULT_SHIELDEDTXFEE_K;
744 
745  // load recipient
746  QList<SendCoinsRecipient> recipients;
747  SendCoinsRecipient recipient;
748  recipient.amount = availableBalance - nPayFee;
749  recipient.isShieldedAddr = true;
750  recipients.append(recipient); // address is added later on, when the wallet is unlocked
751 
752  // Ask if the user want to do it
753  if (!ask(tr("Shield Coins"),
754  tr("You are just about to anonymize all of your balance!\nAvailable %1\nWith fee %2\n\n"
755  "Meaning that you will be able to perform completely\nanonymous transactions"
756  "\n\nDo you want to continue?\n").arg(GUIUtil::formatBalanceWithoutHtml(recipient.amount, nDisplayUnit, false))
757  .arg(GUIUtil::formatBalanceWithoutHtml(nPayFee, nDisplayUnit, false))
758  )) {
759  return;
760  }
761 
762  // Process spending
763  ProcessSend(recipients, true, [this](QList<SendCoinsRecipient>& recipients) {
764  auto res = walletModel->getNewShieldedAddress("");
765  if (!res) {
766  inform(tr("Error generating address to shield PIVs"));
767  return false;
768  }
769  recipients.back().address = QString::fromStdString(res.getObjResult()->ToString());
771  return true;
772  });
773  } else {
774  inform(tr("You don't have any transparent PIVs to shield."));
775  }
776 }
777 
779 {
780  if (!coinControlDialog) return;
782  QMutableListIterator<SendMultiRow*> it(entries);
783  while (it.hasNext()) {
784  const auto& entry = it.next();
785  coinControlDialog->addPayAmount(entry->getAmountValue(), entry->getValue().isShieldedAddr);
786  }
787 }
788 
790 {
792 }
793 
795 {
796  const bool checked = ui->checkBoxDelegations->isChecked();
797  if (checked != fDelegationsChecked) {
798  fDelegationsChecked = checked;
800  }
801 }
802 
803 void SendWidget::onPIVSelected(bool _isTransparent)
804 {
805  if (isTransparent != _isTransparent) {
806  isTransparent = _isTransparent;
811  }
812 }
813 
815 {
816  focusedEntry = entry;
817  if (menu && menu->isVisible()) {
818  menu->hide();
819  }
820 
821  int contactsSize = walletModel->getAddressTableModel()->sizeSend() +
823  if (contactsSize == 0) {
824  inform(tr("No contacts available, you can go to the contacts screen and add some there!"));
825  return;
826  }
827 
828  int height = (contactsSize <= 2) ? entry->getEditHeight() * ( 2 * (contactsSize + 1 )) : entry->getEditHeight() * 6;
829  int width = entry->getEditWidth();
830 
831  if (!menuContacts) {
833  width,
834  height,
835  this
836  );
838  connect(menuContacts, &ContactsDropdown::contactSelected, [this](QString address, QString label) {
839  if (focusedEntry) {
840  if (label != "(no label)")
841  focusedEntry->setLabel(label);
842  focusedEntry->setAddress(address);
843  }
844  });
845 
846  }
847 
848  if (menuContacts->isVisible()) {
849  menuContacts->hide();
850  return;
851  }
852 
853  menuContacts->resizeList(width, height);
854  menuContacts->setStyleSheet(this->styleSheet());
855  menuContacts->adjustSize();
856 
857  QPoint pos;
858  if (entries.size() > 1) {
859  pos = entry->pos();
860  pos.setY((pos.y() + (focusedEntry->getEditHeight() - 12) * 4));
861  } else {
862  pos = focusedEntry->getEditLineRect().bottomLeft();
863  pos.setY((pos.y() + (focusedEntry->getEditHeight() - 12) * 3));
864  }
865  pos.setX(pos.x() + 20);
866  menuContacts->move(pos);
867  menuContacts->show();
868 }
869 
871 {
872  focusedEntry = entry;
873  if (menuContacts && menuContacts->isVisible()) {
874  menuContacts->hide();
875  }
876  QPoint pos = entry->pos();
877  pos.setX(pos.x() + (entry->width() - entry->getMenuBtnWidth()));
878  pos.setY(pos.y() + entry->height() + (entry->getMenuBtnWidth()));
879 
880  if (!this->menu) {
881  this->menu = new TooltipMenu(window, this);
882  this->menu->setCopyBtnText(tr("Add Memo"));
883  this->menu->setEditBtnText(tr("Save contact"));
884  this->menu->setLastBtnVisible(true);
885  this->menu->setLastBtnText(tr("Subtract fee"));
886  this->menu->setMinimumHeight(157);
887  this->menu->setMinimumSize(this->menu->width() + 30, this->menu->height());
888  connect(this->menu, &TooltipMenu::message, this, &AddressesWidget::message);
893  } else {
894  this->menu->hide();
895  }
896  this->menu->setLastBtnCheckable(true, entry->getSubtractFeeFromAmount());
897  menu->move(pos);
898  menu->show();
899 }
900 
902 {
903  if (focusedEntry) {
904  QString address = focusedEntry->getAddress();
905  if (address.isEmpty()) {
906  inform(tr("Address field is empty"));
907  return;
908  }
909 
910  bool isStaking = false, isExchange = false, isShielded = false;
911  auto pivAdd = Standard::DecodeDestination(address.toStdString(), isStaking, isExchange, isShielded);
912 
913  if (!Standard::IsValidDestination(pivAdd) || isStaking) {
914  inform(tr("Invalid address"));
915  return;
916  }
917 
918  if (walletModel->isMine(pivAdd)) {
919  inform(tr("Cannot store your own address as contact"));
920  return;
921  }
922 
923  showHideOp(true);
925  QString label = walletModel->getAddressTableModel()->labelForAddress(address);
926  if (!label.isNull()) {
927  dialog->setTexts(tr("Update Contact"), "Edit label for the selected address:\n%1");
928  dialog->setData(address, label);
929  } else {
930  dialog->setTexts(tr("Create New Contact"), "Save label for the selected address:\n%1");
931  dialog->setData(address, "");
932  }
934  if (dialog->res) {
935  if (label == dialog->getLabel()) {
936  return;
937  }
938  if (walletModel->updateAddressBookLabels(pivAdd, dialog->getLabel().toStdString(),
940  inform(tr("New Contact Stored"));
941  } else {
942  inform(tr("Error Storing Contact"));
943  }
944  }
945  dialog->deleteLater();
946  }
947 
948 }
949 
951 {
952  if (focusedEntry) {
954  menu->setCopyBtnText(tr("Memo"));
955  }
956 }
957 
959 {
960  if (focusedEntry) {
962  }
963 }
964 
966 {
967  if (focusedEntry) {
968  focusedEntry->hide();
969  focusedEntry->deleteLater();
970  int entryNumber = focusedEntry->getNumber();
971 
972  // remove selected entry and update row number for the others
973  QMutableListIterator<SendMultiRow*> it(entries);
974  while (it.hasNext()) {
975  SendMultiRow* entry = it.next();
976  if (focusedEntry == entry) {
977  it.remove();
978  } else if (focusedEntry && entry->getNumber() > entryNumber) {
979  entry->setNumber(entry->getNumber() - 1);
980  }
981  }
982 
983  if (entries.size() == 1) {
984  SendMultiRow* sendMultiRow = QMutableListIterator<SendMultiRow*>(entries).next();
985  sendMultiRow->setNumber(entries.length());
986  sendMultiRow->showLabels();
987  }
988 
989  focusedEntry = nullptr;
990 
991  // Update total amounts
994  }
995 }
996 
998 {
999  if (menuContacts && menuContacts->isVisible() && focusedEntry) {
1000  int width = focusedEntry->getEditWidth();
1001  menuContacts->resizeList(width, menuContacts->height());
1002  menuContacts->resize(width, menuContacts->height());
1003  QPoint pos = focusedEntry->getEditLineRect().bottomLeft();
1004  pos.setX(pos.x() + 20);
1005  pos.setY(pos.y() + ((focusedEntry->getEditHeight() - 12) * 3));
1006  menuContacts->move(pos);
1007  }
1008 }
1009 
1010 void SendWidget::setCustomFeeSelected(bool isSelected, const CAmount& customFee)
1011 {
1012  isCustomFeeSelected = isSelected;
1013  ui->pushButtonFee->setText(isCustomFeeSelected ?
1014  tr("Custom Fee %1").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, customFee) + "/kB") :
1015  tr("Customize Fee"));
1016  if (walletModel)
1017  walletModel->setWalletDefaultFee(customFee);
1018 }
1019 
1020 void SendWidget::changeTheme(bool isLightTheme, QString& theme)
1021 {
1022  coinControlDialog->setStyleSheet(theme);
1023 }
1024 
1026 {
1027  delete ui;
1028  delete coinControlDialog;
1029 }
int64_t CAmount
Amount in PIV (Can be negative)
Definition: amount.h:13
void setTexts(QString title, const char *message=nullptr)
void setData(QString address, QString label)
static const QString ShieldedSend
Specifies shielded receive address.
int sizeShieldedSend() const
static const QString Send
Specifies send address.
QString labelForAddress(const QString &address) const
static QString format(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard, bool cleanRemainderZeros=true)
Format as string.
static QString formatWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string (with unit)
MessageBoxFlags
Flags for CClientUIInterface::ThreadSafeMessageBox.
Definition: guiinterface.h:35
bool HasSelected() const
Definition: coincontrol.h:66
void ListSelected(std::vector< OutPointWrapper > &vOutpoints) const
Definition: coincontrol.h:91
CTxDestination destChange
Definition: coincontrol.h:38
void SetNull()
Definition: coincontrol.h:55
Optional< libzcash::SaplingPaymentAddress > destShieldChange
Definition: coincontrol.h:37
CAmount GetFeePerK() const
Definition: feerate.h:29
void numBlocksChanged(int count)
void setSelectionType(bool isTransparent)
void setModel(WalletModel *model)
CCoinControl * coinControl
void addPayAmount(const CAmount &amount, bool isShieldedRecipient)
void resizeList(int minWidth, int mintHeight)
void contactSelected(QString address, QString label)
void setWalletModel(WalletModel *_model, const QStringList &type)
void execute(Runnable *runnable, int type, std::unique_ptr< WalletModel::UnlockContext > pctx=nullptr)
QString getURI()
std::string getError() const
void clicked()
int getDisplayUnit()
Definition: optionsmodel.h:74
PIVX GUI main class.
Definition: pivxgui.h:46
void showHide(bool show)
Definition: pivxgui.cpp:579
PIVXGUI * window
Definition: pwidget.h:59
void inform(const QString &message)
Definition: pwidget.cpp:45
WalletModel * walletModel
Definition: pwidget.h:61
void setWalletModel(WalletModel *model)
Definition: pwidget.cpp:27
ClientModel * clientModel
Definition: pwidget.h:60
bool execute(int type, std::unique_ptr< WalletModel::UnlockContext > pctx=nullptr)
Definition: pwidget.cpp:94
void showHideOp(bool show)
Definition: pwidget.cpp:40
void message(const QString &title, const QString &body, unsigned int style, bool *ret=nullptr)
bool ask(const QString &title, const QString &message)
Definition: pwidget.cpp:55
CWDestination getDestination() const
void setAddress(QString address)
bool launchMemoDialog()
void setLabel(const QString &label)
void toggleSubtractFeeFromAmount()
void setNumber(int number)
bool getSubtractFeeFromAmount() const
QRect getEditLineRect()
QString getAddress()
void setAmount(const QString &amount)
void setAddressAndLabelOrDescription(const QString &address, const QString &message)
void onMenuClicked(SendMultiRow *entry)
void onValueChanged()
void onContactsClicked(SendMultiRow *entry)
CAmount getAmountValue()
int getMenuBtnWidth()
void setAddress(const QString &address)
void onSubtractFeeFromAmountChecked()
Definition: send.cpp:958
void refreshAmounts()
Definition: send.cpp:140
bool sendFinalStep()
Definition: send.cpp:513
void onChangeAddressClicked()
Definition: send.cpp:611
void onCoinControlClicked()
Definition: send.cpp:700
std::atomic< bool > processingResult
Definition: send.h:104
WalletModelTransaction * ptrModelTx
Definition: send.h:101
void updateAmounts(const QString &titleTotalRemaining, const QString &labelAmountSend, const QString &labelAmountRemaining, CAmount _delegationBalance)
Definition: send.cpp:190
void onResetCustomOptions(bool fRefreshAmounts)
Definition: send.cpp:263
void onContactsClicked(SendMultiRow *entry)
Definition: send.cpp:814
void onDeleteClicked()
Definition: send.cpp:965
bool fDelegationsChecked
Definition: send.h:94
bool isCustomFeeSelected
Definition: send.h:93
Optional< QString > processingResultError
Definition: send.h:103
void setCoinControlPayAmounts()
Definition: send.cpp:778
ContactsDropdown * menuContacts
Definition: send.h:109
void resizeMenu()
Definition: send.cpp:997
void hideContactsMenu()
Definition: send.cpp:240
void onSendClicked()
Definition: send.cpp:378
void showHideCheckBoxDelegations(CAmount delegationBalance)
Definition: send.cpp:362
QPushButton * coinIcon
Definition: send.h:90
void clearAll(bool fClearSettings=true)
Definition: send.cpp:247
CoinControlDialog * coinControlDialog
Definition: send.h:98
SendMultiRow * focusedEntry
Definition: send.h:112
std::atomic< bool > isUpdatingBalance
Definition: send.h:107
void onMenuClicked(SendMultiRow *entry)
Definition: send.cpp:870
void onPIVSelected(bool _isTransparent)
Definition: send.cpp:803
OperationResult prepareShielded(WalletModelTransaction *tx, bool fromTransparent)
Definition: send.cpp:475
void resetCoinControl()
Definition: send.cpp:273
void onShieldCoinsClicked()
Definition: send.cpp:716
OperationResult prepareTransparent(WalletModelTransaction *tx)
Definition: send.cpp:483
void clearEntries()
Definition: send.cpp:288
void resetChangeAddress()
Definition: send.cpp:279
void addEntry()
Definition: send.cpp:299
SendMultiRow * createEntry()
Definition: send.cpp:320
void setFocusOnLastEntry()
Definition: send.cpp:357
SendWidget(PIVXGUI *parent)
Definition: send.cpp:31
TooltipMenu * menu
Definition: send.h:110
void onValueChanged()
Definition: send.cpp:789
void loadClientModel() override
Definition: send.cpp:202
void onChangeCustomFeeClicked()
Definition: send.cpp:688
SendCustomFeeDialog * customFeeDialog
Definition: send.h:92
void showEvent(QShowEvent *event) override
Definition: send.cpp:350
void onError(QString error, int type) override
Definition: send.cpp:576
void resizeEvent(QResizeEvent *event) override
Definition: send.cpp:344
void onAddEntryClicked()
Definition: send.cpp:332
void run(int type) override
Definition: send.cpp:549
void ProcessSend(QList< SendCoinsRecipient > &recipients, bool hasShieldedOutput, const std::function< bool(QList< SendCoinsRecipient > &)> &func=nullptr)
Definition: send.cpp:420
void tryRefreshAmounts()
Definition: send.cpp:582
void onCheckBoxChanged()
Definition: send.cpp:794
void onContactMultiClicked()
Definition: send.cpp:901
void setCustomFeeSelected(bool isSelected, const CAmount &customFee=DEFAULT_TRANSACTION_FEE)
Definition: send.cpp:1010
void updateEntryLabels(const QList< SendCoinsRecipient > &recipients)
Definition: send.cpp:589
void receivedURI(const QString &uri)
Signal raised when a URI was entered or dragged to the GUI.
QList< SendMultiRow * > entries
Definition: send.h:97
void onOpenUriClicked()
Definition: send.cpp:652
void onEntryMemoClicked()
Definition: send.cpp:950
std::atomic< bool > isProcessing
Definition: send.h:102
~SendWidget()
Definition: send.cpp:1025
void changeTheme(bool isLightTheme, QString &theme) override
Definition: send.cpp:1020
Ui::send * ui
Definition: send.h:89
bool isTransparent
Definition: send.h:114
void onResetSettings()
Definition: send.cpp:256
void loadWalletModel() override
Definition: send.cpp:211
int nDisplayUnit
Definition: send.h:96
void setEditBtnText(const QString &btnText)
Definition: tooltipmenu.cpp:26
void onDeleteClicked()
void setLastBtnVisible(bool visible)
Definition: tooltipmenu.cpp:61
void setLastBtnCheckable(bool checkable, bool isChecked)
Definition: tooltipmenu.cpp:43
void setCopyBtnText(const QString &btnText)
Definition: tooltipmenu.cpp:34
void onEditClicked()
void setLastBtnText(const QString &btnText, int minHeight=30)
Definition: tooltipmenu.cpp:38
void onCopyClicked()
void onLastClicked()
WalletModel::SendCoinsReturn getStatus()
void setDisplayUnit(int unit)
void setData(WalletModel *model, WalletModelTransaction *tx)
bool validateAddress(const QString &address)
bool isMine(const CWDestination &address)
bool updateAddressBookLabels(const CWDestination &address, const std::string &strName, const std::string &strPurpose)
bool isSaplingInMaintenance() const
Definition: walletmodel.cpp:96
CAmount getLockedBalance(bool isTransparent) const
void listCoins(std::map< ListCoinsKey, std::vector< ListCoinsValue >> &mapCoins, bool fSelectTransparent) const
void setWalletCustomFee(bool fUseCustomFee, const CAmount nFee=DEFAULT_TRANSACTION_FEE)
CallResult< Destination > getNewShieldedAddress(std::string strLabel="")
Return a new shielded address.
bool getWalletCustomFee(CAmount &nFeeRet)
OperationResult PrepareShieldedTransaction(WalletModelTransaction *modelTransaction, bool fromTransparent, const CCoinControl *coinControl=nullptr)
AddressTableModel * getAddressTableModel()
OptionsModel * getOptionsModel()
SendCoinsReturn prepareTransaction(WalletModelTransaction *transaction, const CCoinControl *coinControl=nullptr, bool fIncludeDelegations=true)
interfaces::WalletBalances GetWalletBalances()
Definition: walletmodel.h:165
void setWalletDefaultFee(CAmount fee=DEFAULT_TRANSACTION_FEE)
CAmount getBalance(const CCoinControl *coinControl=nullptr, bool fIncludeDelegated=true, bool fUnlockedOnly=false, bool fIncludeShielded=true) const
UnlockContext requestUnlock()
Data model for a walletmodel transaction.
QList< SendCoinsRecipient > getRecipients()
Sapling functions.
Definition: address.h:30
boost::variant< CTxDestination, libzcash::SaplingPaymentAddress > CWDestination
const std::string CURRENCY_UNIT
Definition: feerate.cpp:11
QString formatBalanceWithoutHtml(CAmount amount, int nDisplayUnit, bool isZpiv)
Definition: guiutil.cpp:124
QString formatBalance(CAmount amount, int nDisplayUnit, bool isZpiv)
Definition: guiutil.cpp:119
bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
Definition: guiutil.cpp:156
void ProcessSendCoinsReturnAndInform(PWidget *parent, const WalletModel::SendCoinsReturn &sendCoinsReturn, WalletModel *walletModel, const QString &msgArg, bool fPrepare)
QString ProcessSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, WalletModel *walletModel, CClientUIInterface::MessageBoxFlags &informType, const QString &msgArg, bool fPrepare)
std::string EncodePaymentAddress(const libzcash::PaymentAddress &zaddr)
const CTxDestination * GetTransparentDestination(const CWDestination &dest)
bool IsValidDestination(const CWDestination &address)
std::string EncodeDestination(const CWDestination &address, const CChainParams::Base58Type addrType)
CWDestination DecodeDestination(const std::string &strAddress)
const libzcash::SaplingPaymentAddress * GetShieldedDestination(const CWDestination &dest)
OperationResult errorOut(const std::string &errorStr)
bool isLightTheme()
Definition: qtutils.cpp:210
bool openDialogWithOpaqueBackgroundFullScreen(QDialog *widget, PIVXGUI *gui)
Definition: qtutils.cpp:81
bool openDialogWithOpaqueBackgroundY(QDialog *widget, PIVXGUI *gui, double posX, int posY, bool hideOpaqueBackground)
Definition: qtutils.cpp:59
void setCssProperty(std::initializer_list< QWidget * > args, const QString &value)
Definition: qtutils.cpp:334
void setCssBtnPrimary(QPushButton *btn, bool forceUpdate)
Definition: qtutils.cpp:302
void setCssBtnSecondary(QPushButton *btn, bool forceUpdate)
Definition: qtutils.cpp:307
void updateStyle(QWidget *widget)
Definition: qtutils.cpp:225
#define BINDINGSIG_SIZE
#define CTXIN_SPEND_DUST_SIZE
#define OUTPUTDESCRIPTION_SIZE
#define REQUEST_PREPARE_TX
Definition: send.cpp:28
#define REQUEST_REFRESH_BALANCE
Definition: send.cpp:29
boost::variant< CNoDestination, CKeyID, CScriptID, CExchangeKeyID > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:72
Collection of wallet balances.
Definition: wallet.h:16
bool error(const char *fmt, const Args &... args)
Definition: system.h:77
CAmount GetMinRelayFee(const CTransaction &tx, const CTxMemPool &pool, unsigned int nBytes)
Definition: validation.cpp:265