/* * This file is part of the Flowee project * Copyright (C) 2020-2021 Tom Zander * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "PaymentDetailOutput_p.h" #include "PaymentDetailInputs_p.h" #include "FloweePay.h" #include "AccountInfo.h" #include #include PaymentDetailOutput::PaymentDetailOutput(Payment *parent) : PaymentDetail(parent, Payment::PayToAddress) { } double PaymentDetailOutput::paymentAmount() const { if (!m_fiatFollows) { Payment *p = qobject_cast(parent()); assert(p); if (p->fiatPrice() == 0) return 0; return static_cast(m_fiatAmount * 100000000L / p->fiatPrice()); } if (m_maxAllowed && m_maxSelected) { /* * If the value is 'max' then we think per definition in satoshis. * We will count up all the outputs and subtract that from the base amount. * * The base amount is either the total spendable money in the wallet, or in * case there is an InputsSelector, the total amount selected there. */ Payment *p = qobject_cast(parent()); assert(p); assert(p->currentAccount()); auto wallet = p->currentAccount()->wallet(); assert(wallet); int64_t baseBalance = wallet->balanceConfirmed() + wallet->balanceUnconfirmed(); int64_t outputs = 0; for (auto detail : p->m_paymentDetails) { if (detail == this) // max is only allowed in the last of the outputs continue; if (detail->isOutput()) outputs += detail->toOutput()->paymentAmount(); if (detail->isInputs()) baseBalance = detail->toInputs()->selectedValue(); } return static_cast(std::max(0l, baseBalance - outputs)); } return static_cast(m_paymentAmount); } void PaymentDetailOutput::setPaymentAmount(double amount_) { qint64 amount = static_cast(amount_); if (m_paymentAmount == amount) return; m_paymentAmount = amount; // implicit changes first, it changes the representation setFiatFollows(true); setMaxSelected(false); emit fiatAmountChanged(); emit paymentAmountChanged(); checkValid(); } const QString &PaymentDetailOutput::address() const { return m_address; } void PaymentDetailOutput::setAddress(const QString &address_) { const QString address = address_.trimmed(); if (m_address == address) return; m_address = address; switch (FloweePay::instance()->identifyString(address)) { case FloweePay::LegacyPKH: { CBase58Data legacy; auto ok = legacy.SetString(m_address.toStdString()); assert(ok); assert(legacy.isMainnetPkh() || legacy.isTestnetPkh()); CashAddress::Content c; c.hash = legacy.data(); c.type = CashAddress::PUBKEY_TYPE; m_formattedTarget = QString::fromStdString(CashAddress::encodeCashAddr(chainPrefix(), c)); break; } case FloweePay::CashPKH: { auto c = CashAddress::decodeCashAddrContent(m_address.toStdString(), chainPrefix()); assert (!c.hash.empty() && c.type == CashAddress::PUBKEY_TYPE); m_formattedTarget = QString::fromStdString(CashAddress::encodeCashAddr(chainPrefix(), c)); break; } case FloweePay::LegacySH: { CBase58Data legacy; auto ok = legacy.SetString(m_address.toStdString()); assert(ok); assert(legacy.isMainnetSh() || legacy.isTestnetSh()); CashAddress::Content c; c.hash = legacy.data(); c.type = CashAddress::SCRIPT_TYPE; m_formattedTarget = QString::fromStdString(CashAddress::encodeCashAddr(chainPrefix(), c)); break; } case FloweePay::CashSH: { auto c = CashAddress::decodeCashAddrContent(m_address.toStdString(), chainPrefix()); assert (!c.hash.empty() && c.type == CashAddress::SCRIPT_TYPE); m_formattedTarget = QString::fromStdString(CashAddress::encodeCashAddr(chainPrefix(), c)); break; } default: m_formattedTarget.clear(); } emit addressChanged(); checkValid(); } void PaymentDetailOutput::checkValid() { bool valid = !m_formattedTarget.isEmpty(); valid = valid && ((m_maxSelected && m_maxAllowed) || (m_fiatFollows && m_paymentAmount > 600) || (!m_fiatFollows && m_fiatAmount > 0)); setValid(valid); } bool PaymentDetailOutput::maxSelected() const { return m_maxSelected; } void PaymentDetailOutput::setMaxSelected(bool on) { if (m_maxSelected == on) return; m_maxSelected = on; // implicit change first, it changes the representation setFiatFollows(on); emit maxSelectedChanged(); emit fiatAmountChanged(); emit paymentAmountChanged(); checkValid(); } void PaymentDetailOutput::recalcMax() { if (m_maxSelected && m_maxAllowed) { emit fiatAmountChanged(); emit paymentAmountChanged(); } } bool PaymentDetailOutput::fiatFollows() const { return m_fiatFollows; } void PaymentDetailOutput::setFiatFollows(bool on) { if (m_fiatFollows == on) return; m_fiatFollows = on; emit fiatFollowsChanged(); } int PaymentDetailOutput::fiatAmount() const { if (m_fiatFollows) { Payment *p = qobject_cast(parent()); assert(p); if (p->fiatPrice() == 0) return 0; qint64 amount = m_paymentAmount; if (m_maxAllowed && m_maxSelected) { assert(p->currentAccount()); auto wallet = p->currentAccount()->wallet(); assert(wallet); int64_t baseBalance = wallet->balanceConfirmed() + wallet->balanceUnconfirmed(); int64_t outputs = 0; for (auto detail : p->m_paymentDetails) { if (detail == this) // max is only allowed in the last of the outputs continue; if (detail->isOutput()) outputs += detail->toOutput()->paymentAmount(); if (detail->isInputs()) baseBalance = detail->toInputs()->selectedValue(); } amount = std::max(0l, baseBalance - outputs); } return (amount * p->fiatPrice() / 10000000 + 5) / 10; } return m_fiatAmount; } void PaymentDetailOutput::setFiatAmount(int amount) { if (m_fiatAmount == amount) return; m_fiatAmount = amount; // implicit changes first, it changes the representation setFiatFollows(false); setMaxSelected(false); emit fiatAmountChanged(); emit paymentAmountChanged(); checkValid(); } const QString &PaymentDetailOutput::formattedTarget() const { return m_formattedTarget; } bool PaymentDetailOutput::maxAllowed() const { return m_maxAllowed; } void PaymentDetailOutput::setMaxAllowed(bool max) { if (m_maxAllowed == max) return; m_maxAllowed = max; emit maxAllowedChanged(); if (max == false && m_paymentAmount == -1) setPaymentAmount(0); }