Support fee-subtracting sendmany payouts
This commit is contained in:
@@ -914,9 +914,9 @@ UniValue sendfrom(const UniValue& params, bool fHelp)
|
||||
|
||||
UniValue sendmany(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() < 2 || params.size() > 4)
|
||||
if (fHelp || params.size() < 2 || params.size() > 8)
|
||||
throw runtime_error(
|
||||
"sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" )\n"
|
||||
"sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" subtractfeefrom )\n"
|
||||
"\nSend multiple times. Amounts are double-precision floating point numbers." +
|
||||
HelpRequiringPassphrase() + "\n"
|
||||
|
||||
@@ -929,6 +929,8 @@ UniValue sendmany(const UniValue& params, bool fHelp)
|
||||
" }\n"
|
||||
"3. minconf (numeric, optional, default=1) Only use the balance confirmed at least this many times.\n"
|
||||
"4. \"comment\" (string, optional) A comment\n"
|
||||
"5. subtractfeefrom (array, optional) A json array with addresses that pay the fee\n"
|
||||
"\nAlso accepts Dash/PIVX-style sendmany arguments where comment is argument 5 and subtractfeefrom is argument 6.\n"
|
||||
|
||||
"\nResult:\n"
|
||||
"\"transactionid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n"
|
||||
@@ -952,15 +954,31 @@ UniValue sendmany(const UniValue& params, bool fHelp)
|
||||
|
||||
CWalletTx wtx;
|
||||
wtx.strFromAccount = strAccount;
|
||||
if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty())
|
||||
wtx.mapValue["comment"] = params[3].get_str();
|
||||
int nCommentIndex = 3;
|
||||
int nSubtractFeeFromIndex = 4;
|
||||
if (params.size() > 3 && params[3].isBool()) {
|
||||
nCommentIndex = 4;
|
||||
nSubtractFeeFromIndex = 5;
|
||||
}
|
||||
|
||||
if (params.size() > nCommentIndex && !params[nCommentIndex].isNull() && params[nCommentIndex].isStr() && !params[nCommentIndex].get_str().empty())
|
||||
wtx.mapValue["comment"] = params[nCommentIndex].get_str();
|
||||
|
||||
set<CBitcoinAddress> setAddress;
|
||||
set<string> setSubtractFeeFromAddress;
|
||||
set<int> setSubtractFeeFromOutputs;
|
||||
vector<pair<CScript, CAmount> > vecSend;
|
||||
|
||||
if (params.size() > nSubtractFeeFromIndex && !params[nSubtractFeeFromIndex].isNull()) {
|
||||
UniValue subtractFeeFrom = params[nSubtractFeeFromIndex].get_array();
|
||||
for (unsigned int idx = 0; idx < subtractFeeFrom.size(); idx++)
|
||||
setSubtractFeeFromAddress.insert(subtractFeeFrom[idx].get_str());
|
||||
}
|
||||
|
||||
CAmount totalAmount = 0;
|
||||
vector<string> keys = sendTo.getKeys();
|
||||
for (const string& name_ : keys) {
|
||||
for (unsigned int i = 0; i < keys.size(); i++) {
|
||||
const string& name_ = keys[i];
|
||||
CBitcoinAddress address(name_);
|
||||
if (!address.IsValid())
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Agrarian address: ")+name_);
|
||||
@@ -974,6 +992,14 @@ UniValue sendmany(const UniValue& params, bool fHelp)
|
||||
totalAmount += nAmount;
|
||||
|
||||
vecSend.push_back(make_pair(scriptPubKey, nAmount));
|
||||
|
||||
if (setSubtractFeeFromAddress.count(name_))
|
||||
setSubtractFeeFromOutputs.insert(i);
|
||||
}
|
||||
|
||||
for (const string& name_ : setSubtractFeeFromAddress) {
|
||||
if (!sendTo.exists(name_))
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, subtract fee from address not in send set: ")+name_);
|
||||
}
|
||||
|
||||
EnsureWalletIsUnlocked();
|
||||
@@ -987,7 +1013,7 @@ UniValue sendmany(const UniValue& params, bool fHelp)
|
||||
CReserveKey keyChange(pwalletMain);
|
||||
CAmount nFeeRequired = 0;
|
||||
string strFailReason;
|
||||
bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, strFailReason);
|
||||
bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, strFailReason, NULL, ALL_COINS, false, 0, &setSubtractFeeFromOutputs);
|
||||
if (!fCreated)
|
||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
|
||||
if (!pwalletMain->CommitTransaction(wtx, keyChange))
|
||||
|
||||
+44
-10
@@ -2094,11 +2094,13 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
|
||||
const CCoinControl* coinControl,
|
||||
AvailableCoinsType coin_type,
|
||||
bool useIX,
|
||||
CAmount nFeePay)
|
||||
CAmount nFeePay,
|
||||
const std::set<int>* setSubtractFeeFromOutputs)
|
||||
{
|
||||
if (useIX && nFeePay < CENT) nFeePay = CENT;
|
||||
|
||||
CAmount nValue = 0;
|
||||
const bool fSubtractFeeFromAmount = setSubtractFeeFromOutputs && !setSubtractFeeFromOutputs->empty();
|
||||
|
||||
for (const PAIRTYPE(CScript, CAmount) & s : vecSend) {
|
||||
if (nValue < 0) {
|
||||
@@ -2126,13 +2128,29 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
|
||||
txNew.vout.clear();
|
||||
wtxNew.fFromMe = true;
|
||||
|
||||
CAmount nTotalValue = nValue + nFeeRet;
|
||||
CAmount nTotalValue = nValue + (fSubtractFeeFromAmount ? 0 : nFeeRet);
|
||||
double dPriority = 0;
|
||||
|
||||
// vouts to the payees
|
||||
if (coinControl && !coinControl->fSplitBlock) {
|
||||
for (const PAIRTYPE(CScript, CAmount) & s : vecSend) {
|
||||
CTxOut txout(s.second, s.first);
|
||||
for (unsigned int i = 0; i < vecSend.size(); i++) {
|
||||
const PAIRTYPE(CScript, CAmount)& s = vecSend[i];
|
||||
CAmount nAmount = s.second;
|
||||
|
||||
if(fSubtractFeeFromAmount && setSubtractFeeFromOutputs->count(i)) {
|
||||
CAmount nSubtractFee = nFeeRet / setSubtractFeeFromOutputs->size();
|
||||
if (i == (unsigned int)*setSubtractFeeFromOutputs->begin())
|
||||
nSubtractFee += nFeeRet % setSubtractFeeFromOutputs->size();
|
||||
|
||||
nAmount -= nSubtractFee;
|
||||
}
|
||||
|
||||
if (nAmount <= 0) {
|
||||
strFailReason = _("The transaction amount is too small to pay the fee");
|
||||
return false;
|
||||
}
|
||||
|
||||
CTxOut txout(nAmount, s.first);
|
||||
if (txout.IsDust(::minRelayTxFee)) {
|
||||
strFailReason = _("Transaction amount too small");
|
||||
return false;
|
||||
@@ -2148,13 +2166,29 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
|
||||
else
|
||||
nSplitBlock = 1;
|
||||
|
||||
for (const PAIRTYPE(CScript, CAmount) & s : vecSend) {
|
||||
for (int i = 0; i < nSplitBlock; i++) {
|
||||
if (i == nSplitBlock - 1) {
|
||||
uint64_t nRemainder = s.second % nSplitBlock;
|
||||
txNew.vout.push_back(CTxOut((s.second / nSplitBlock) + nRemainder, s.first));
|
||||
for (unsigned int i = 0; i < vecSend.size(); i++) {
|
||||
const PAIRTYPE(CScript, CAmount)& s = vecSend[i];
|
||||
CAmount nAmount = s.second;
|
||||
|
||||
if(fSubtractFeeFromAmount && setSubtractFeeFromOutputs->count(i)) {
|
||||
CAmount nSubtractFee = nFeeRet / setSubtractFeeFromOutputs->size();
|
||||
if (i == (unsigned int)*setSubtractFeeFromOutputs->begin())
|
||||
nSubtractFee += nFeeRet % setSubtractFeeFromOutputs->size();
|
||||
|
||||
nAmount -= nSubtractFee;
|
||||
}
|
||||
|
||||
if (nAmount <= 0) {
|
||||
strFailReason = _("The transaction amount is too small to pay the fee");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int j = 0; j < nSplitBlock; j++) {
|
||||
if (j == nSplitBlock - 1) {
|
||||
uint64_t nRemainder = nAmount % nSplitBlock;
|
||||
txNew.vout.push_back(CTxOut((nAmount / nSplitBlock) + nRemainder, s.first));
|
||||
} else
|
||||
txNew.vout.push_back(CTxOut(s.second / nSplitBlock, s.first));
|
||||
txNew.vout.push_back(CTxOut(nAmount / nSplitBlock, s.first));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -500,7 +500,8 @@ public:
|
||||
const CCoinControl* coinControl = NULL,
|
||||
AvailableCoinsType coin_type = ALL_COINS,
|
||||
bool useIX = false,
|
||||
CAmount nFeePay = 0);
|
||||
CAmount nFeePay = 0,
|
||||
const std::set<int>* setSubtractFeeFromOutputs = NULL);
|
||||
bool CreateTransaction(CScript scriptPubKey, const CAmount& nValue, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl = NULL, AvailableCoinsType coin_type = ALL_COINS, bool useIX = false, CAmount nFeePay = 0);
|
||||
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, std::string strCommand = "tx");
|
||||
bool AddAccountingEntry(const CAccountingEntry&, CWalletDB & pwalletdb);
|
||||
|
||||
Reference in New Issue
Block a user