Skip to content

Commit

Permalink
multi: Allow subtracting fee from output
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
sefbkn committed Feb 17, 2022
1 parent b5fa3cc commit 2ac3e38
Show file tree
Hide file tree
Showing 9 changed files with 341 additions and 33 deletions.
15 changes: 10 additions & 5 deletions internal/rpc/jsonrpc/methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -3525,7 +3525,7 @@ func makeOutputs(pairs map[string]dcrutil.Amount, chainParams *chaincfg.Params)
// sendPairs creates and sends payment transactions.
// It returns the transaction hash in string format upon success
// All errors are returned in dcrjson.RPCError format
func (s *Server) sendPairs(ctx context.Context, w *wallet.Wallet, amounts map[string]dcrutil.Amount, account uint32, minconf int32) (string, error) {
func (s *Server) sendPairs(ctx context.Context, w *wallet.Wallet, amounts map[string]dcrutil.Amount, account uint32, minconf int32, recipientPaysFee bool) (string, error) {
changeAccount := account
if s.cfg.CSPPServer != "" && s.cfg.MixAccount != "" && s.cfg.MixChangeAccount != "" {
mixAccount, err := w.AccountNumber(ctx, s.cfg.MixAccount)
Expand Down Expand Up @@ -4409,7 +4409,7 @@ func (s *Server) sendFrom(ctx context.Context, icmd interface{}) (interface{}, e
cmd.ToAddress: amt,
}

return s.sendPairs(ctx, w, pairs, account, minConf)
return s.sendPairs(ctx, w, pairs, account, minConf, false)
}

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

return s.sendPairs(ctx, w, pairs, account, minConf)
return s.sendPairs(ctx, w, pairs, account, minConf, false)
}

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

// sendtoaddress always spends from the default account, this matches bitcoind
return s.sendPairs(ctx, w, pairs, udb.DefaultAccountNum, 1)
recipientPaysFee := false
if cmd.SubtractFeeFromAmount != nil {
recipientPaysFee = *cmd.SubtractFeeFromAmount
}

// sendtoaddress always spends from the default account.
return s.sendPairs(ctx, w, pairs, udb.DefaultAccountNum, 1, recipientPaysFee)
}

// sendToMultiSig handles a sendtomultisig RPC request by creating a new
Expand Down
4 changes: 2 additions & 2 deletions internal/rpc/jsonrpc/rpcserverhelp.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func helpDescsEnUS() map[string]string {
"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",
"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",
"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",
"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",
"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",
"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",
"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",
"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",
Expand Down Expand Up @@ -115,4 +115,4 @@ var localeHelpDescs = map[string]func() map[string]string{
"en_US": helpDescsEnUS,
}

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\""
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\""
11 changes: 6 additions & 5 deletions internal/rpchelp/helpdescs_en_US.go
Original file line number Diff line number Diff line change
Expand Up @@ -760,11 +760,12 @@ var helpDescsEnUS = map[string]string{
"sendtoaddress--synopsis": "Authors, signs, and sends a transaction that outputs some amount to a payment address.\n" +
"Unlike sendfrom, outputs are always chosen from the default account.\n" +
"A change output is automatically included to send extra output value back to the original account.",
"sendtoaddress-address": "Address to pay",
"sendtoaddress-amount": "Amount to send to the payment address valued in decred",
"sendtoaddress-comment": "Unused",
"sendtoaddress-commentto": "Unused",
"sendtoaddress--result0": "The transaction hash of the sent transaction",
"sendtoaddress-address": "Address to pay",
"sendtoaddress-amount": "Amount to send to the payment address valued in decred",
"sendtoaddress-comment": "Unused",
"sendtoaddress-commentto": "Unused",
"sendtoaddress-subtractfeefromamount": "Toggles whether the tx fee is subtracted from the payment rather than the change",
"sendtoaddress--result0": "The transaction hash of the sent transaction",

// SendToMultisigCmd help.
"sendtomultisig--synopsis": "Authors, signs, and sends a transaction that outputs some amount to a multisig address.\n" +
Expand Down
9 changes: 5 additions & 4 deletions rpc/jsonrpc/types/methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -830,10 +830,11 @@ func NewSendManyCmd(fromAccount string, amounts map[string]float64, minConf *int

// SendToAddressCmd defines the sendtoaddress JSON-RPC command.
type SendToAddressCmd struct {
Address string
Amount float64
Comment *string
CommentTo *string
Address string
Amount float64
Comment *string
CommentTo *string
SubtractFeeFromAmount *bool `jsonrpcdefault:"false"`
}

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

0 comments on commit 2ac3e38

Please sign in to comment.