1 #include <qt/splitutxopage.h>
2 #include <qt/forms/ui_splitutxopage.h>
3
4 #include <qt/bitcoinunits.h>
5 #include <qt/execrpccommand.h>
6 #include <qt/guiutil.h>
7 #include <qt/optionsmodel.h>
8 #include <qt/sendcoinsdialog.h>
9 #include <qt/walletmodel.h>
10 #include <validation.h>
11
12 namespace SplitUTXO_NS
13 {
14 static const QString PRC_COMMAND = "splitutxosforaddress";
15 static const QString PARAM_ADDRESS = "address";
16 static const QString PARAM_MIN_VALUE = "minValue";
17 static const QString PARAM_MAX_VALUE = "maxValue";
18 static const QString PARAM_MAX_OUTPUTS = "maxOutputs";
19 }
20 using namespace SplitUTXO_NS;
21
SplitUTXOPage(QWidget * parent,Mode mode)22 SplitUTXOPage::SplitUTXOPage(QWidget *parent, Mode mode) :
23 QDialog(parent),
24 ui(new Ui::SplitUTXOPage),
25 m_model(nullptr),
26 m_mode(mode)
27 {
28 ui->setupUi(this);
29
30 switch (m_mode) {
31 case Normal:
32 setWindowTitle(tr("Split coins for address"));
33 ui->labelAddress->setText(tr("Address"));
34 break;
35
36 case Delegation:
37 setWindowTitle(tr("Split coins for offline staking"));
38 ui->labelAddress->setText(tr("Delegate address"));
39 ui->labelDescription->setText(tr("Split coins for offline staking."));
40 break;
41
42 case SuperStaker:
43 setWindowTitle(tr("Split coins for super staker"));
44 ui->labelAddress->setText(tr("Staker address"));
45 ui->labelDescription->setText(tr("Split coins for super staker. The UTXO value need to be minimum <b> %1 </b>.").
46 arg(BitcoinUnits::formatHtmlWithUnit(BitcoinUnits::BTC, DEFAULT_STAKING_MIN_UTXO_VALUE)));
47 ui->lineEditMinValue->SetMinValue(DEFAULT_STAKING_MIN_UTXO_VALUE);
48 ui->lineEditMaxValue->SetMinValue(DEFAULT_STAKING_MIN_UTXO_VALUE);
49 break;
50 }
51
52 ui->labelAddress->setToolTip(tr("The qtum address to split utxos."));
53 ui->labelMinValue->setToolTip(tr("Select utxo which value is smaller than value (minimum 0.1 COIN)."));
54 ui->labelMaxValue->setToolTip(tr("Select utxo which value is greater than value (minimum 0.1 COIN)."));
55 ui->labelMaxOutputs->setToolTip(tr("Maximum outputs to create"));
56
57 ui->lineEditAddress->setSenderAddress(true);
58 ui->txtAddress->setReadOnly(true);
59 ui->txtAddress->setVisible(false);
60
61 QFont font = QApplication::font();
62 font.setPointSizeF(font.pointSizeF() * 0.8);
63 ui->labelDescription->setFont(font);
64
65 // Set defaults
66 ui->lineEditMinValue->setValue(DEFAULT_STAKING_MIN_UTXO_VALUE);
67 ui->lineEditMaxValue->setValue(DEFAULT_STAKING_MIN_UTXO_VALUE * 2);
68 ui->spinBoxMaxOutputs->setMinimum(1);
69 ui->spinBoxMaxOutputs->setMaximum(10000);
70 ui->spinBoxMaxOutputs->setValue(100);
71
72 ui->splitCoinsButton->setEnabled(false);
73
74 // Create new PRC command line interface
75 QStringList lstMandatory;
76 lstMandatory.append(PARAM_ADDRESS);
77 lstMandatory.append(PARAM_MIN_VALUE);
78 lstMandatory.append(PARAM_MAX_VALUE);
79
80 QStringList lstOptional;
81 lstOptional.append(PARAM_MAX_OUTPUTS);
82
83 QMap<QString, QString> lstTranslations;
84 lstTranslations[PARAM_ADDRESS] = ui->labelAddress->text();
85 lstTranslations[PARAM_MIN_VALUE] = ui->labelMinValue->text();
86 lstTranslations[PARAM_MAX_VALUE] = ui->labelMaxValue->text();
87 lstTranslations[PARAM_MAX_OUTPUTS] = ui->labelMaxOutputs->text();
88
89 m_execRPCCommand = new ExecRPCCommand(PRC_COMMAND, lstMandatory, lstOptional, lstTranslations, this);
90
91 connect(ui->splitCoinsButton, &QPushButton::clicked, this, &SplitUTXOPage::on_splitCoinsClicked);
92 connect(ui->cancelButton, &QPushButton::clicked, this, &SplitUTXOPage::on_cancelButtonClicked);
93 connect(ui->lineEditAddress, &QComboBox::currentTextChanged, this, &SplitUTXOPage::on_updateSplitCoinsButton);
94 }
95
~SplitUTXOPage()96 SplitUTXOPage::~SplitUTXOPage()
97 {
98 delete ui;
99 }
100
setModel(WalletModel * _model)101 void SplitUTXOPage::setModel(WalletModel *_model)
102 {
103 m_model = _model;
104 ui->lineEditAddress->setWalletModel(m_model);
105
106 if (m_model && m_model->getOptionsModel())
107 connect(m_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &SplitUTXOPage::updateDisplayUnit);
108
109 // update the display unit, to not use the default ("QTUM")
110 updateDisplayUnit();
111 }
112
setAddress(const QString & address)113 void SplitUTXOPage::setAddress(const QString &address)
114 {
115 ui->lineEditAddress->setVisible(false);
116 ui->txtAddress->setVisible(true);
117 ui->txtAddress->setText(address);
118
119 if(m_mode == Normal)
120 setWindowTitle(tr("Split coins for address %1").arg(address));
121
122 ui->splitCoinsButton->setEnabled(true);
123 }
124
isDataValid()125 bool SplitUTXOPage::isDataValid()
126 {
127 bool dataValid = true;
128 ui->lineEditMinValue->setValid(true);
129 ui->lineEditMaxValue->setValid(true);
130
131 CAmount minValue = ui->lineEditMinValue->value();
132 CAmount maxValue = ui->lineEditMaxValue->value();
133
134 if(ui->lineEditAddress->isVisible() && !ui->lineEditAddress->isValidAddress())
135 dataValid = false;
136 if(minValue < COIN/10)
137 {
138 ui->lineEditMinValue->setValid(false);
139 dataValid = false;
140 }
141 if(maxValue < COIN/10)
142 {
143 ui->lineEditMaxValue->setValid(false);
144 dataValid = false;
145 }
146 if(minValue > COIN/10 && maxValue > COIN/10 && minValue > maxValue)
147 {
148 ui->lineEditMinValue->setValid(false);
149 ui->lineEditMaxValue->setValid(false);
150 dataValid = false;
151 }
152
153 return dataValid;
154 }
155
clearAll()156 void SplitUTXOPage::clearAll()
157 {
158 if(ui->lineEditAddress->isVisible())
159 {
160 ui->lineEditAddress->setCurrentIndex(-1);
161 ui->splitCoinsButton->setEnabled(false);
162 }
163 ui->lineEditMinValue->setValue(DEFAULT_STAKING_MIN_UTXO_VALUE);
164 ui->lineEditMaxValue->setValue(DEFAULT_STAKING_MIN_UTXO_VALUE * 2);
165 ui->spinBoxMaxOutputs->setValue(100);
166 }
167
accept()168 void SplitUTXOPage::accept()
169 {
170 clearAll();
171 QDialog::accept();
172 }
173
reject()174 void SplitUTXOPage::reject()
175 {
176 clearAll();
177 QDialog::reject();
178 }
179
updateDisplayUnit()180 void SplitUTXOPage::updateDisplayUnit()
181 {
182 if(m_model && m_model->getOptionsModel())
183 {
184 // Update min and max value with the current unit
185 ui->lineEditMinValue->setDisplayUnit(m_model->getOptionsModel()->getDisplayUnit());
186 ui->lineEditMaxValue->setDisplayUnit(m_model->getOptionsModel()->getDisplayUnit());
187 }
188 }
189
on_splitCoinsClicked()190 void SplitUTXOPage::on_splitCoinsClicked()
191 {
192 if(m_model && m_model->getOptionsModel())
193 {
194 if(!isDataValid())
195 return;
196
197 // Initialize variables
198 QMap<QString, QString> lstParams;
199 QVariant result;
200 QString errorMessage;
201 QString resultJson;
202 int unit = BitcoinUnits::BTC;
203 QString address = ui->lineEditAddress->isVisible() ? ui->lineEditAddress->currentText() : ui->txtAddress->text();
204 CAmount minValue = ui->lineEditMinValue->value();
205 CAmount maxValue = ui->lineEditMaxValue->value();
206 CAmount maxOutputs = ui->spinBoxMaxOutputs->value();
207
208 // Append params to the list
209 ExecRPCCommand::appendParam(lstParams, PARAM_ADDRESS, address);
210 ExecRPCCommand::appendParam(lstParams, PARAM_MIN_VALUE, BitcoinUnits::format(unit, minValue, false, BitcoinUnits::separatorNever));
211 ExecRPCCommand::appendParam(lstParams, PARAM_MAX_VALUE, BitcoinUnits::format(unit, maxValue, false, BitcoinUnits::separatorNever));
212 ExecRPCCommand::appendParam(lstParams, PARAM_MAX_OUTPUTS, QString::number(maxOutputs));
213
214 QString questionString = tr("Are you sure you want to split coins for address");
215 questionString.append(QString("<br/><br/><b>%1</b>?")
216 .arg(address));
217
218 SendConfirmationDialog confirmationDialog(tr("Confirm splitting coins for address."), questionString, "", "", SEND_CONFIRM_DELAY, tr("Send"), this);
219 confirmationDialog.exec();
220
221 QMessageBox::StandardButton retval = (QMessageBox::StandardButton)confirmationDialog.result();
222 if(retval == QMessageBox::Yes)
223 {
224 if(!m_execRPCCommand->exec(m_model->node(), m_model, lstParams, result, resultJson, errorMessage))
225 {
226 QMessageBox::warning(this, tr("Split coins for address"), errorMessage);
227 }
228 else
229 {
230 QVariantMap variantMap = result.toMap();
231
232 QString selectedString = variantMap.value("selected").toString();
233 CAmount selected;
234 BitcoinUnits::parse(unit, selectedString, &selected);
235
236 QString splitedString = variantMap.value("splited").toString();
237 CAmount splited;
238 BitcoinUnits::parse(unit, splitedString, &splited);
239
240 int displayUnit = m_model->getOptionsModel()->getDisplayUnit();
241
242 QString infoString = tr("Selected: %1 less than %2 and above of %3.").
243 arg(BitcoinUnits::formatHtmlWithUnit(displayUnit, selected)).
244 arg(BitcoinUnits::formatHtmlWithUnit(displayUnit, minValue)).
245 arg(BitcoinUnits::formatHtmlWithUnit(displayUnit, maxValue));
246 infoString.append("<br/><br/>");
247 infoString.append(tr("Splitted: %1.").arg(BitcoinUnits::formatHtmlWithUnit(displayUnit, splited)));
248
249 QMessageBox::information(this, tr("Split coins for address"), infoString);
250
251 if(splited == selected || splited == 0)
252 accept();
253 }
254 }
255 }
256 }
257
on_cancelButtonClicked()258 void SplitUTXOPage::on_cancelButtonClicked()
259 {
260 reject();
261 }
262
on_updateSplitCoinsButton()263 void SplitUTXOPage::on_updateSplitCoinsButton()
264 {
265 bool enabled = true;
266 bool validAddress = !ui->lineEditAddress->isVisible() || (ui->lineEditAddress->isVisible() && !ui->lineEditAddress->currentText().isEmpty());
267
268 if(!validAddress)
269 {
270 enabled = false;
271 }
272
273 ui->splitCoinsButton->setEnabled(enabled);
274 }
275