mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-21 07:39:08 +01:00
Subtract fee from amount
Fixes #2724 and #1570. Adds the automatically-subtract-the-fee-from-the-amount-and-send-whats-left feature to the GUI and RPC (sendtoaddress,sendmany).
This commit is contained in:
committed by
Wladimir J. van der Laan
parent
90a43c1e93
commit
292623adf5
@@ -1549,21 +1549,22 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
|
||||
(bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
|
||||
CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl)
|
||||
bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend,
|
||||
CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason, const CCoinControl* coinControl)
|
||||
{
|
||||
CAmount nValue = 0;
|
||||
BOOST_FOREACH (const PAIRTYPE(CScript, CAmount)& s, vecSend)
|
||||
unsigned int nSubtractFeeFromAmount = 0;
|
||||
BOOST_FOREACH (const CRecipient& recipient, vecSend)
|
||||
{
|
||||
if (nValue < 0)
|
||||
if (nValue < 0 || recipient.nAmount < 0)
|
||||
{
|
||||
strFailReason = _("Transaction amounts must be positive");
|
||||
return false;
|
||||
}
|
||||
nValue += s.second;
|
||||
nValue += recipient.nAmount;
|
||||
|
||||
if (recipient.fSubtractFeeFromAmount)
|
||||
nSubtractFeeFromAmount++;
|
||||
}
|
||||
if (vecSend.empty() || nValue < 0)
|
||||
{
|
||||
@@ -1606,16 +1607,40 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
|
||||
txNew.vin.clear();
|
||||
txNew.vout.clear();
|
||||
wtxNew.fFromMe = true;
|
||||
nChangePosRet = -1;
|
||||
bool fFirst = true;
|
||||
|
||||
CAmount nTotalValue = nValue + nFeeRet;
|
||||
CAmount nTotalValue = nValue;
|
||||
if (nSubtractFeeFromAmount == 0)
|
||||
nTotalValue += nFeeRet;
|
||||
double dPriority = 0;
|
||||
// vouts to the payees
|
||||
BOOST_FOREACH (const PAIRTYPE(CScript, CAmount)& s, vecSend)
|
||||
BOOST_FOREACH (const CRecipient& recipient, vecSend)
|
||||
{
|
||||
CTxOut txout(s.second, s.first);
|
||||
CTxOut txout(recipient.nAmount, recipient.scriptPubKey);
|
||||
|
||||
if (recipient.fSubtractFeeFromAmount)
|
||||
{
|
||||
txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient
|
||||
|
||||
if (fFirst) // first receiver pays the remainder not divisible by output count
|
||||
{
|
||||
fFirst = false;
|
||||
txout.nValue -= nFeeRet % nSubtractFeeFromAmount;
|
||||
}
|
||||
}
|
||||
|
||||
if (txout.IsDust(::minRelayTxFee))
|
||||
{
|
||||
strFailReason = _("Transaction amount too small");
|
||||
if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)
|
||||
{
|
||||
if (txout.nValue < 0)
|
||||
strFailReason = _("The transaction amount is too small to pay the fee");
|
||||
else
|
||||
strFailReason = _("The transaction amount is too small to send after the fee has been deducted");
|
||||
}
|
||||
else
|
||||
strFailReason = _("Transaction amount too small");
|
||||
return false;
|
||||
}
|
||||
txNew.vout.push_back(txout);
|
||||
@@ -1642,7 +1667,9 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
|
||||
dPriority += (double)nCredit * age;
|
||||
}
|
||||
|
||||
CAmount nChange = nValueIn - nValue - nFeeRet;
|
||||
CAmount nChange = nValueIn - nValue;
|
||||
if (nSubtractFeeFromAmount == 0)
|
||||
nChange -= nFeeRet;
|
||||
|
||||
if (nChange > 0)
|
||||
{
|
||||
@@ -1676,6 +1703,28 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
|
||||
|
||||
CTxOut newTxOut(nChange, scriptChange);
|
||||
|
||||
// We do not move dust-change to fees, because the sender would end up paying more than requested.
|
||||
// This would be against the purpose of the all-inclusive feature.
|
||||
// So instead we raise the change and deduct from the recipient.
|
||||
if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(::minRelayTxFee))
|
||||
{
|
||||
CAmount nDust = newTxOut.GetDustThreshold(::minRelayTxFee) - newTxOut.nValue;
|
||||
newTxOut.nValue += nDust; // raise change until no more dust
|
||||
for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient
|
||||
{
|
||||
if (vecSend[i].fSubtractFeeFromAmount)
|
||||
{
|
||||
txNew.vout[i].nValue -= nDust;
|
||||
if (txNew.vout[i].IsDust(::minRelayTxFee))
|
||||
{
|
||||
strFailReason = _("The transaction amount is too small to send after the fee has been deducted");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Never create dust outputs; if we would, just
|
||||
// add the dust to the fee.
|
||||
if (newTxOut.IsDust(::minRelayTxFee))
|
||||
@@ -1686,7 +1735,8 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
|
||||
else
|
||||
{
|
||||
// Insert change txn at random position:
|
||||
vector<CTxOut>::iterator position = txNew.vout.begin()+GetRandInt(txNew.vout.size()+1);
|
||||
nChangePosRet = GetRandInt(txNew.vout.size()+1);
|
||||
vector<CTxOut>::iterator position = txNew.vout.begin()+nChangePosRet;
|
||||
txNew.vout.insert(position, newTxOut);
|
||||
}
|
||||
}
|
||||
@@ -1755,15 +1805,8 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CWallet::CreateTransaction(CScript scriptPubKey, const CAmount& nValue,
|
||||
CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl)
|
||||
{
|
||||
vector< pair<CScript, CAmount> > vecSend;
|
||||
vecSend.push_back(make_pair(scriptPubKey, nValue));
|
||||
return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet, strFailReason, coinControl);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user