Skip to content
This repository was archived by the owner on Dec 5, 2020. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 67 additions & 9 deletions src/bitpaywalletclient/bpwalletclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,23 +339,47 @@ bool BitPayWalletClient::GetLastKnownAddress(std::string& address, std::string&
}


bool BitPayWalletClient::CreatePaymentProposal(const std::string& address, uint64_t amount, uint64_t feeperkb, UniValue& paymentProposalOut, std::string& errorOut)
bool BitPayWalletClient::CreatePaymentProposal(const std::string& address, uint64_t amount, uint64_t feeperkb, UniValue& paymentProposalOut, std::string& errorOut, bool checkSingleOutput)
{
//form request
UniValue outputs(UniValue::VARR);

//currently we only support a single output
UniValue mainOutput(UniValue::VOBJ);

// amount == 0 indicates to send max
uint64_t amount_to_send = amount;
if (amount == 0) {
std::string walletResponse;
if (!GetWallets(walletResponse)) {
errorOut = "Could not load balance";
return false;
}

UniValue walletResponseUni;
walletResponseUni.read(walletResponse);

UniValue balanceObj = find_value(walletResponseUni, "balance");
if (!balanceObj.isObject()) {
errorOut = "Could not load balance";
return false;
}
UniValue totalAmountUni = find_value(balanceObj, "totalAmount");
if (!totalAmountUni.isNum()) {
errorOut = "Could not load balance";
return false;
}
amount_to_send = totalAmountUni.get_int64();
}
//add the output
mainOutput.push_back(Pair("toAddress", address));
mainOutput.push_back(Pair("amount", amount));
mainOutput.push_back(Pair("amount", amount_to_send));
mainOutput.push_back(Pair("message", ""));
mainOutput.push_back(Pair("script", ""));
outputs.push_back(mainOutput);

UniValue jsonArgs(UniValue::VOBJ);
jsonArgs.push_back(Pair("feePerKb", feeperkb));
jsonArgs.push_back(Pair("feePerKb", (amount != 0 ? feeperkb : 0)));
jsonArgs.push_back(Pair("payProUrl", false));
jsonArgs.push_back(Pair("type", "simple"));
jsonArgs.push_back(Pair("version", "1.0.0"));
Expand Down Expand Up @@ -406,25 +430,59 @@ bool BitPayWalletClient::CreatePaymentProposal(const std::string& address, uint6
}
paymentProposalOut.read(response);

if (!PublishTxProposal(paymentProposalOut, errorOut))
return false;

return true;
} else {
//unknown error
UniValue messageUni;
messageUni = find_value(responseUni, "message");
codeUni = find_value(responseUni, "code");

//
if (codeUni.isStr() && codeUni.get_str() == "INSUFFICIENT_FUNDS_FOR_FEE" && checkSingleOutput) {
return CreatePaymentProposal(address, amount_to_send-1000, feeperkb, paymentProposalOut, errorOut, true);
}


if (messageUni.isStr())
errorOut = messageUni.get_str();

return false;
}
} else {
paymentProposalOut.read(response);
}

if (amount == 0) {
// send max proposal

if (paymentProposalOut.isObject()) {
// count inputs
UniValue inputsObj = find_value(paymentProposalOut, "inputs");
std::vector<UniValue> inputs = inputsObj.getValues();

// assume 149 bytes per input (32 txid, 4 pos, 74 der sig inc. push, 35 pubkey inc. push, 4 nSequence)
// assume 34 bytes per output (25+1 P2PKH, 8 amount,
// assume 10 bytes for rest of tx

uint64_t estimatedSize = 10 + 34 + (149 * inputs.size());
uint64_t feeWillBe = (estimatedSize/1000.0)*feeperkb;

// BWS min fee calculation is buggy, "looping down" the value is required
return CreatePaymentProposal(address, amount_to_send - feeWillBe, feeperkb, paymentProposalOut, errorOut, true);
}
}

paymentProposalOut.read(response);
if (!paymentProposalOut.isObject())
return false;

if (checkSingleOutput) {
UniValue outputsObj = find_value(paymentProposalOut, "outputs");
std::vector<UniValue> outputs = outputsObj.getValues();
if (outputs.size() > 1) {
errorOut = "Could not calculate maximal amount to send out";
return false;
}
}

if (!PublishTxProposal(paymentProposalOut, errorOut))
return false;

Expand Down Expand Up @@ -567,7 +625,7 @@ bool BitPayWalletClient::GetFeeLevels()
return false;

long httpStatusCode = 0;
if (!SendRequest("get", "/v1/feelevels/?network=livenet&r="+std::to_string(CheapRandom()), "{}", response, httpStatusCode))
if (!SendRequest("get", "/v1/feelevels/?network="+std::string(testnet ? "testnet" : "livenet")+"&r="+std::to_string(CheapRandom()), "{}", response, httpStatusCode))
return false;

if (httpStatusCode != 200)
Expand Down
4 changes: 3 additions & 1 deletion src/bitpaywalletclient/bpwalletclient.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@ class BitPayWalletClient
//!Return the last (disk) cached known address for receiving coins
bool GetLastKnownAddress(std::string& address, std::string& keypath);

bool CreatePaymentProposal(const std::string& address, uint64_t amount, uint64_t feeperkb, UniValue& paymentProposalOut, std::string& errorOut);
//!Creates a payment proposal
//! If amount is set to 0, it tired to create a proposal with all available funds
bool CreatePaymentProposal(const std::string& address, uint64_t amount, uint64_t feeperkb, UniValue& paymentProposalOut, std::string& errorOut, bool checkSingleOutput = false);

bool PublishTxProposal(const UniValue& paymentProposal, std::string& errorOut);

Expand Down
31 changes: 26 additions & 5 deletions src/qt/dbb_gui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ DBBDaemonGui::DBBDaemonGui(const QString& uri, QWidget* parent) : QMainWindow(pa
connect(ui->upgradeFirmware, SIGNAL(clicked()), this, SLOT(upgradeFirmwareButton()));
connect(ui->openSettings, SIGNAL(clicked()), this, SLOT(showSettings()));
connect(ui->pairDeviceButton, SIGNAL(clicked()), this, SLOT(pairSmartphone()));
connect(ui->sendMax, SIGNAL(stateChanged(int)), this, SLOT(sendMaxCheckboxDidChange(int)));
connect(ui->feeLevel, SIGNAL(currentIndexChanged(int)), this, SLOT(sendMaxCheckboxDidChange(int)));
ui->upgradeFirmware->setVisible(true);
ui->keypathLabel->setVisible(false);//hide keypath label for now (only tooptip)
connect(ui->tableWidget, SIGNAL(doubleClicked(QModelIndex)),this,SLOT(historyShowTx(QModelIndex)));
Expand Down Expand Up @@ -966,6 +968,7 @@ void DBBDaemonGui::modalStateChanged(bool state)
{
this->ui->sendToAddress->setEnabled(!state);
this->ui->sendAmount->setEnabled(!state);
sendMaxCheckboxDidChange(0);
}

void DBBDaemonGui::updateModalWithQRCode(const QString& string)
Expand Down Expand Up @@ -2139,18 +2142,35 @@ void DBBDaemonGui::updateReceivingAddress(DBBWallet *wallet, const std::string &
ui->keypathLabel->setText(QString::fromStdString(info));
}

void DBBDaemonGui::sendMaxCheckboxDidChange(int state)
{
if (this->ui->sendMax->isChecked()) {
this->ui->sendAmount->setText("All funds");
this->ui->sendAmount->setEnabled(false);
}
else {
this->ui->sendAmount->setEnabled(true);
if ( this->ui->sendAmount->text() == "All funds") {
this->ui->sendAmount->setText("");
}
}
int64_t fee = singleWallet->client.GetFeeForPriority(this->ui->feeLevel->currentIndex());
ui->feeInfo->setText(tr("Feerate to use: %1").arg(QString::number(fee/10000000.0)));
}

void DBBDaemonGui::createTxProposalPressed()
{
if (!singleWallet->client.IsSeeded())
return;

int64_t amount = 0;
if (this->ui->sendAmount->text().size() == 0 || !DBB::ParseMoney(this->ui->sendAmount->text().toStdString(), amount))
return showAlert("Error", "Invalid amount");

if (cachedDeviceLock && !comServer->mobileAppConnected)
return showAlert("Error", "2FA enabled but no mobile app found online");
if (!this->ui->sendMax->isChecked()) {
if (this->ui->sendAmount->text().size() == 0 || !DBB::ParseMoney(this->ui->sendAmount->text().toStdString(), amount))
return showAlert("Error", "Invalid amount");

if (cachedDeviceLock && !comServer->mobileAppConnected)
return showAlert("Error", "2FA enabled but no mobile app found online");
}
this->ui->sendToAddress->clearFocus();
this->ui->sendAmount->clearFocus();
DBBNetThread* thread = DBBNetThread::DetachThread();
Expand Down Expand Up @@ -2540,6 +2560,7 @@ void DBBDaemonGui::executeNetUpdateWallet(DBBWallet* wallet, bool showLoading, s
wallet->shouldUpdateWalletAgain = false;
walletsAvailable = wallet->client.GetWallets(walletsResponse);
wallet->client.GetFeeLevels();
this->sendMaxCheckboxDidChange(0);
std::string txHistoryResponse;
if (wallet == this->singleWallet) {
bool transactionHistoryAvailable = wallet->client.GetTransactionHistory(txHistoryResponse);
Expand Down
1 change: 1 addition & 0 deletions src/qt/dbb_gui.h
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ private slots:
//!gets called when a new address is available
void updateReceivingAddress(DBBWallet *wallet, const std::string &newAddress, const std::string &keypath);
//!check the UI values and create a payment proposal from them, sign and post them
void sendMaxCheckboxDidChange(int val);
void createTxProposalPressed();
//!Report about a submitted payment proposal
void reportPaymentProposalPost(DBBWallet* wallet, const UniValue& proposal);
Expand Down
21 changes: 21 additions & 0 deletions src/qt/ui/overview.ui
Original file line number Diff line number Diff line change
Expand Up @@ -1270,6 +1270,27 @@ background-color: rgba(240,240,240,255);
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_12">
<property name="topMargin">
<number>10</number>
</property>
<item>
<widget class="QLabel" name="feeInfo">
<property name="text">
<string>Fee</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="sendMax">
<property name="text">
<string>Send all available funds</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
Expand Down