Skip to content

Commit 2ac3e38

Browse files
committed
multi: Allow subtracting fee from output
This change adds functionality that allows a user to have the transaction fee subtracted from the amount sent to a recipient rather than from a change output.
1 parent b5fa3cc commit 2ac3e38

File tree

9 files changed

+341
-33
lines changed

9 files changed

+341
-33
lines changed

internal/rpc/jsonrpc/methods.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3525,7 +3525,7 @@ func makeOutputs(pairs map[string]dcrutil.Amount, chainParams *chaincfg.Params)
35253525
// sendPairs creates and sends payment transactions.
35263526
// It returns the transaction hash in string format upon success
35273527
// All errors are returned in dcrjson.RPCError format
3528-
func (s *Server) sendPairs(ctx context.Context, w *wallet.Wallet, amounts map[string]dcrutil.Amount, account uint32, minconf int32) (string, error) {
3528+
func (s *Server) sendPairs(ctx context.Context, w *wallet.Wallet, amounts map[string]dcrutil.Amount, account uint32, minconf int32, recipientPaysFee bool) (string, error) {
35293529
changeAccount := account
35303530
if s.cfg.CSPPServer != "" && s.cfg.MixAccount != "" && s.cfg.MixChangeAccount != "" {
35313531
mixAccount, err := w.AccountNumber(ctx, s.cfg.MixAccount)
@@ -4409,7 +4409,7 @@ func (s *Server) sendFrom(ctx context.Context, icmd interface{}) (interface{}, e
44094409
cmd.ToAddress: amt,
44104410
}
44114411

4412-
return s.sendPairs(ctx, w, pairs, account, minConf)
4412+
return s.sendPairs(ctx, w, pairs, account, minConf, false)
44134413
}
44144414

44154415
// sendMany handles a sendmany RPC request by creating a new transaction
@@ -4451,7 +4451,7 @@ func (s *Server) sendMany(ctx context.Context, icmd interface{}) (interface{}, e
44514451
pairs[k] = amt
44524452
}
44534453

4454-
return s.sendPairs(ctx, w, pairs, account, minConf)
4454+
return s.sendPairs(ctx, w, pairs, account, minConf, false)
44554455
}
44564456

44574457
// sendToAddress handles a sendtoaddress RPC request by creating a new
@@ -4487,8 +4487,13 @@ func (s *Server) sendToAddress(ctx context.Context, icmd interface{}) (interface
44874487
cmd.Address: amt,
44884488
}
44894489

4490-
// sendtoaddress always spends from the default account, this matches bitcoind
4491-
return s.sendPairs(ctx, w, pairs, udb.DefaultAccountNum, 1)
4490+
recipientPaysFee := false
4491+
if cmd.SubtractFeeFromAmount != nil {
4492+
recipientPaysFee = *cmd.SubtractFeeFromAmount
4493+
}
4494+
4495+
// sendtoaddress always spends from the default account.
4496+
return s.sendPairs(ctx, w, pairs, udb.DefaultAccountNum, 1, recipientPaysFee)
44924497
}
44934498

44944499
// sendToMultiSig handles a sendtomultisig RPC request by creating a new

internal/rpc/jsonrpc/rpcserverhelp.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func helpDescsEnUS() map[string]string {
7878
"sendfromtreasury": "sendfromtreasury \"key\" amounts\n\nSend from treasury balance to multiple recipients.\n\nArguments:\n1. key (string, required) Politeia public key\n2. amounts (object, required) Pairs of payment addresses and the output amount to pay each\n{\n \"Address to pay\": Amount to send to the payment address valued in decred, (object) JSON object using payment addresses as keys and output amounts valued in decred to send to each address\n ...\n}\n\nResult:\n\"value\" (string) The transaction hash of the sent transaction\n",
7979
"sendmany": "sendmany \"fromaccount\" {\"address\":amount,...} (minconf=1 \"comment\")\n\nAuthors, signs, and sends a transaction that outputs to many payment addresses.\nA change output is automatically included to send extra output value back to the original account.\n\nArguments:\n1. fromaccount (string, required) Account to pick unspent outputs from\n2. amounts (object, required) Pairs of payment addresses and the output amount to pay each\n{\n \"Address to pay\": Amount to send to the payment address valued in decred, (object) JSON object using payment addresses as keys and output amounts valued in decred to send to each address\n ...\n}\n3. minconf (numeric, optional, default=1) Minimum number of block confirmations required before a transaction output is eligible to be spent\n4. comment (string, optional) Unused\n\nResult:\n\"value\" (string) The transaction hash of the sent transaction\n",
8080
"sendrawtransaction": "sendrawtransaction \"hextx\" (allowhighfees=false)\n\nSubmits the serialized, hex-encoded transaction to the local peer and relays it to the network.\n\nArguments:\n1. hextx (string, required) Serialized, hex-encoded signed transaction\n2. allowhighfees (boolean, optional, default=false) Whether or not to allow insanely high fees\n\nResult:\n\"value\" (string) The transaction hash of the sent transaction\n",
81-
"sendtoaddress": "sendtoaddress \"address\" amount (\"comment\" \"commentto\")\n\nAuthors, signs, and sends a transaction that outputs some amount to a payment address.\nUnlike sendfrom, outputs are always chosen from the default account.\nA change output is automatically included to send extra output value back to the original account.\n\nArguments:\n1. address (string, required) Address to pay\n2. amount (numeric, required) Amount to send to the payment address valued in decred\n3. comment (string, optional) Unused\n4. commentto (string, optional) Unused\n\nResult:\n\"value\" (string) The transaction hash of the sent transaction\n",
81+
"sendtoaddress": "sendtoaddress \"address\" amount (\"comment\" \"commentto\" subtractfeefromamount=false)\n\nAuthors, signs, and sends a transaction that outputs some amount to a payment address.\nUnlike sendfrom, outputs are always chosen from the default account.\nA change output is automatically included to send extra output value back to the original account.\n\nArguments:\n1. address (string, required) Address to pay\n2. amount (numeric, required) Amount to send to the payment address valued in decred\n3. comment (string, optional) Unused\n4. commentto (string, optional) Unused\n5. subtractfeefromamount (boolean, optional, default=false) Toggles whether the tx fee is subtracted from the payment rather than the change\n\nResult:\n\"value\" (string) The transaction hash of the sent transaction\n",
8282
"sendtomultisig": "sendtomultisig \"fromaccount\" amount [\"pubkey\",...] (nrequired=1 minconf=1 \"comment\")\n\nAuthors, signs, and sends a transaction that outputs some amount to a multisig address.\nUnlike sendfrom, outputs are always chosen from the default account.\nA change output is automatically included to send extra output value back to the original account.\n\nArguments:\n1. fromaccount (string, required) Unused\n2. amount (numeric, required) Amount to send to the payment address valued in decred\n3. pubkeys (array of string, required) Pubkey to send to.\n4. nrequired (numeric, optional, default=1) The number of signatures required to redeem outputs paid to this address\n5. minconf (numeric, optional, default=1) Minimum number of block confirmations required\n6. comment (string, optional) Unused\n\nResult:\n\"value\" (string) The transaction hash of the sent transaction\n",
8383
"sendtotreasury": "sendtotreasury amount\n\nSend decred to treasury\n\nArguments:\n1. amount (numeric, required) Amount to send to treasury\n\nResult:\n\"value\" (string) The transaction hash of the sent transaction\n",
8484
"setaccountpassphrase": "setaccountpassphrase \"account\" \"passphrase\"\n\nIndividually encrypt or change per-account passphrase\n\nArguments:\n1. account (string, required) Account to modify\n2. passphrase (string, required) New passphrase to use.\nIf this is the empty string, the account passphrase is removed and the account becomes encrypted by the global wallet passhprase.\n\nResult:\nNothing\n",
@@ -115,4 +115,4 @@ var localeHelpDescs = map[string]func() map[string]string{
115115
"en_US": helpDescsEnUS,
116116
}
117117

118-
var requestUsages = "abandontransaction \"hash\"\naccountaddressindex \"account\" branch\naccountsyncaddressindex \"account\" branch index\naccountunlocked \"account\"\naddmultisigaddress nrequired [\"key\",...] (\"account\")\naddtransaction \"blockhash\" \"transaction\"\nauditreuse (since)\nconsolidate inputs (\"account\" \"address\")\ncreatemultisig nrequired [\"key\",...]\ncreatenewaccount \"account\"\ncreaterawtransaction [{\"amount\":n.nnn,\"txid\":\"value\",\"vout\":n,\"tree\":n},...] {\"address\":amount,...} (locktime expiry)\ncreatesignature \"address\" inputindex hashtype \"previouspkscript\" \"serializedtransaction\"\ndisapprovepercent\ndiscoverusage (\"startblock\" discoveraccounts gaplimit)\ndumpprivkey \"address\"\nfundrawtransaction \"hexstring\" \"fundaccount\" ({\"changeaddress\":changeaddress,\"feerate\":feerate,\"conftarget\":conftarget})\ngeneratevote \"blockhash\" height \"tickethash\" votebits \"votebitsext\"\ngetaccount \"address\"\ngetaccountaddress \"account\"\ngetaddressesbyaccount \"account\"\ngetbalance (\"account\" minconf=1)\ngetbestblock\ngetbestblockhash\ngetblockcount\ngetblockhash index\ngetblockheader \"hash\" (verbose=true)\ngetblock \"hash\" (verbose=true verbosetx=false)\ngetcoinjoinsbyacct\ngetcurrentnet\ngetinfo\ngetmasterpubkey (\"account\")\ngetmultisigoutinfo \"hash\" index\ngetnewaddress (\"account\" \"gappolicy\")\ngetpeerinfo\ngetrawchangeaddress (\"account\")\ngetreceivedbyaccount \"account\" (minconf=1)\ngetreceivedbyaddress \"address\" (minconf=1)\ngetstakeinfo\ngettickets includeimmature\ngettransaction \"txid\" (includewatchonly=false)\ngettxout \"txid\" vout tree (includemempool=true)\ngetunconfirmedbalance (\"account\")\ngetvotechoices (\"tickethash\")\ngetwalletfee\ngetcfilterv2 \"blockhash\"\nhelp (\"command\")\nimportcfiltersv2 startheight [\"filter\",...]\nimportprivkey \"privkey\" (\"label\" rescan=true scanfrom)\nimportscript \"hex\" (rescan=true scanfrom)\nimportxpub \"name\" \"xpub\"\nlistaccounts (minconf=1)\nlistaddresstransactions [\"address\",...] (\"account\")\nlistalltransactions (\"account\")\nlistlockunspent (\"account\")\nlistreceivedbyaccount (minconf=1 includeempty=false includewatchonly=false)\nlistreceivedbyaddress (minconf=1 includeempty=false includewatchonly=false)\nlistsinceblock (\"blockhash\" targetconfirmations=1 includewatchonly=false)\nlisttransactions (\"account\" count=10 from=0 includewatchonly=false)\nlistunspent (minconf=1 maxconf=9999999 [\"address\",...] \"account\")\nlockaccount \"account\"\nlockunspent unlock [{\"amount\":n.nnn,\"txid\":\"value\",\"vout\":n,\"tree\":n},...]\nmixaccount\nmixoutput \"outpoint\"\npurchaseticket \"fromaccount\" spendlimit (minconf=1 \"ticketaddress\" numtickets=1 \"pooladdress\" poolfees expiry \"comment\" dontsigntx)\nprocessunmanagedticket (\"tickethash\")\nredeemmultisigout \"hash\" index tree (\"address\")\nredeemmultisigouts \"fromscraddress\" (\"toaddress\" number)\nrenameaccount \"oldaccount\" \"newaccount\"\nrescanwallet (beginheight=0)\nrevoketickets\nsendfrom \"fromaccount\" \"toaddress\" amount (minconf=1 \"comment\" \"commentto\")\nsendfromtreasury \"key\" amounts\nsendmany \"fromaccount\" {\"address\":amount,...} (minconf=1 \"comment\")\nsendrawtransaction \"hextx\" (allowhighfees=false)\nsendtoaddress \"address\" amount (\"comment\" \"commentto\")\nsendtomultisig \"fromaccount\" amount [\"pubkey\",...] (nrequired=1 minconf=1 \"comment\")\nsendtotreasury amount\nsetaccountpassphrase \"account\" \"passphrase\"\nsetdisapprovepercent percent\nsettreasurypolicy \"key\" \"policy\" (\"ticket\")\nsettspendpolicy \"hash\" \"policy\" (\"ticket\")\nsettxfee amount\nsetvotechoice \"agendaid\" \"choiceid\" (\"tickethash\")\nsignmessage \"address\" \"message\"\nsignrawtransaction \"rawtx\" ([{\"txid\":\"value\",\"vout\":n,\"tree\":n,\"scriptpubkey\":\"value\",\"redeemscript\":\"value\"},...] [\"privkey\",...] flags=\"ALL\")\nsignrawtransactions [\"rawtx\",...] (send=true)\nstakepooluserinfo \"user\"\nsweepaccount \"sourceaccount\" \"destinationaddress\" (requiredconfirmations feeperkb)\nsyncstatus\nticketinfo (startheight=0)\nticketsforaddress \"address\"\ntreasurypolicy (\"key\" \"ticket\")\ntspendpolicy (\"hash\" \"ticket\")\nunlockaccount \"account\" \"passphrase\"\nvalidateaddress \"address\"\nvalidatepredcp0005cf\nverifymessage \"address\" \"signature\" \"message\"\nversion\nwalletinfo\nwalletislocked\nwalletlock\nwalletpassphrase \"passphrase\" timeout\nwalletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\nwalletpubpassphrasechange \"oldpassphrase\" \"newpassphrase\""
118+
var requestUsages = "abandontransaction \"hash\"\naccountaddressindex \"account\" branch\naccountsyncaddressindex \"account\" branch index\naccountunlocked \"account\"\naddmultisigaddress nrequired [\"key\",...] (\"account\")\naddtransaction \"blockhash\" \"transaction\"\nauditreuse (since)\nconsolidate inputs (\"account\" \"address\")\ncreatemultisig nrequired [\"key\",...]\ncreatenewaccount \"account\"\ncreaterawtransaction [{\"amount\":n.nnn,\"txid\":\"value\",\"vout\":n,\"tree\":n},...] {\"address\":amount,...} (locktime expiry)\ncreatesignature \"address\" inputindex hashtype \"previouspkscript\" \"serializedtransaction\"\ndisapprovepercent\ndiscoverusage (\"startblock\" discoveraccounts gaplimit)\ndumpprivkey \"address\"\nfundrawtransaction \"hexstring\" \"fundaccount\" ({\"changeaddress\":changeaddress,\"feerate\":feerate,\"conftarget\":conftarget})\ngeneratevote \"blockhash\" height \"tickethash\" votebits \"votebitsext\"\ngetaccount \"address\"\ngetaccountaddress \"account\"\ngetaddressesbyaccount \"account\"\ngetbalance (\"account\" minconf=1)\ngetbestblock\ngetbestblockhash\ngetblockcount\ngetblockhash index\ngetblockheader \"hash\" (verbose=true)\ngetblock \"hash\" (verbose=true verbosetx=false)\ngetcoinjoinsbyacct\ngetcurrentnet\ngetinfo\ngetmasterpubkey (\"account\")\ngetmultisigoutinfo \"hash\" index\ngetnewaddress (\"account\" \"gappolicy\")\ngetpeerinfo\ngetrawchangeaddress (\"account\")\ngetreceivedbyaccount \"account\" (minconf=1)\ngetreceivedbyaddress \"address\" (minconf=1)\ngetstakeinfo\ngettickets includeimmature\ngettransaction \"txid\" (includewatchonly=false)\ngettxout \"txid\" vout tree (includemempool=true)\ngetunconfirmedbalance (\"account\")\ngetvotechoices (\"tickethash\")\ngetwalletfee\ngetcfilterv2 \"blockhash\"\nhelp (\"command\")\nimportcfiltersv2 startheight [\"filter\",...]\nimportprivkey \"privkey\" (\"label\" rescan=true scanfrom)\nimportscript \"hex\" (rescan=true scanfrom)\nimportxpub \"name\" \"xpub\"\nlistaccounts (minconf=1)\nlistaddresstransactions [\"address\",...] (\"account\")\nlistalltransactions (\"account\")\nlistlockunspent (\"account\")\nlistreceivedbyaccount (minconf=1 includeempty=false includewatchonly=false)\nlistreceivedbyaddress (minconf=1 includeempty=false includewatchonly=false)\nlistsinceblock (\"blockhash\" targetconfirmations=1 includewatchonly=false)\nlisttransactions (\"account\" count=10 from=0 includewatchonly=false)\nlistunspent (minconf=1 maxconf=9999999 [\"address\",...] \"account\")\nlockaccount \"account\"\nlockunspent unlock [{\"amount\":n.nnn,\"txid\":\"value\",\"vout\":n,\"tree\":n},...]\nmixaccount\nmixoutput \"outpoint\"\npurchaseticket \"fromaccount\" spendlimit (minconf=1 \"ticketaddress\" numtickets=1 \"pooladdress\" poolfees expiry \"comment\" dontsigntx)\nprocessunmanagedticket (\"tickethash\")\nredeemmultisigout \"hash\" index tree (\"address\")\nredeemmultisigouts \"fromscraddress\" (\"toaddress\" number)\nrenameaccount \"oldaccount\" \"newaccount\"\nrescanwallet (beginheight=0)\nrevoketickets\nsendfrom \"fromaccount\" \"toaddress\" amount (minconf=1 \"comment\" \"commentto\")\nsendfromtreasury \"key\" amounts\nsendmany \"fromaccount\" {\"address\":amount,...} (minconf=1 \"comment\")\nsendrawtransaction \"hextx\" (allowhighfees=false)\nsendtoaddress \"address\" amount (\"comment\" \"commentto\" subtractfeefromamount=false)\nsendtomultisig \"fromaccount\" amount [\"pubkey\",...] (nrequired=1 minconf=1 \"comment\")\nsendtotreasury amount\nsetaccountpassphrase \"account\" \"passphrase\"\nsetdisapprovepercent percent\nsettreasurypolicy \"key\" \"policy\" (\"ticket\")\nsettspendpolicy \"hash\" \"policy\" (\"ticket\")\nsettxfee amount\nsetvotechoice \"agendaid\" \"choiceid\" (\"tickethash\")\nsignmessage \"address\" \"message\"\nsignrawtransaction \"rawtx\" ([{\"txid\":\"value\",\"vout\":n,\"tree\":n,\"scriptpubkey\":\"value\",\"redeemscript\":\"value\"},...] [\"privkey\",...] flags=\"ALL\")\nsignrawtransactions [\"rawtx\",...] (send=true)\nstakepooluserinfo \"user\"\nsweepaccount \"sourceaccount\" \"destinationaddress\" (requiredconfirmations feeperkb)\nsyncstatus\nticketinfo (startheight=0)\nticketsforaddress \"address\"\ntreasurypolicy (\"key\" \"ticket\")\ntspendpolicy (\"hash\" \"ticket\")\nunlockaccount \"account\" \"passphrase\"\nvalidateaddress \"address\"\nvalidatepredcp0005cf\nverifymessage \"address\" \"signature\" \"message\"\nversion\nwalletinfo\nwalletislocked\nwalletlock\nwalletpassphrase \"passphrase\" timeout\nwalletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\nwalletpubpassphrasechange \"oldpassphrase\" \"newpassphrase\""

internal/rpchelp/helpdescs_en_US.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -760,11 +760,12 @@ var helpDescsEnUS = map[string]string{
760760
"sendtoaddress--synopsis": "Authors, signs, and sends a transaction that outputs some amount to a payment address.\n" +
761761
"Unlike sendfrom, outputs are always chosen from the default account.\n" +
762762
"A change output is automatically included to send extra output value back to the original account.",
763-
"sendtoaddress-address": "Address to pay",
764-
"sendtoaddress-amount": "Amount to send to the payment address valued in decred",
765-
"sendtoaddress-comment": "Unused",
766-
"sendtoaddress-commentto": "Unused",
767-
"sendtoaddress--result0": "The transaction hash of the sent transaction",
763+
"sendtoaddress-address": "Address to pay",
764+
"sendtoaddress-amount": "Amount to send to the payment address valued in decred",
765+
"sendtoaddress-comment": "Unused",
766+
"sendtoaddress-commentto": "Unused",
767+
"sendtoaddress-subtractfeefromamount": "Toggles whether the tx fee is subtracted from the payment rather than the change",
768+
"sendtoaddress--result0": "The transaction hash of the sent transaction",
768769

769770
// SendToMultisigCmd help.
770771
"sendtomultisig--synopsis": "Authors, signs, and sends a transaction that outputs some amount to a multisig address.\n" +

rpc/jsonrpc/types/methods.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -830,10 +830,11 @@ func NewSendManyCmd(fromAccount string, amounts map[string]float64, minConf *int
830830

831831
// SendToAddressCmd defines the sendtoaddress JSON-RPC command.
832832
type SendToAddressCmd struct {
833-
Address string
834-
Amount float64
835-
Comment *string
836-
CommentTo *string
833+
Address string
834+
Amount float64
835+
Comment *string
836+
CommentTo *string
837+
SubtractFeeFromAmount *bool `jsonrpcdefault:"false"`
837838
}
838839

839840
// NewSendToAddressCmd returns a new instance which can be used to issue a

0 commit comments

Comments
 (0)