Skip to content

Commit

Permalink
Add --send-all to kaspawallet send command (#2181)
Browse files Browse the repository at this point in the history
* Allow to send --all

* Fix a typo

---------

Co-authored-by: Ori Newman <orinewman1@gmail.com>
  • Loading branch information
svarogg and someone235 authored Feb 27, 2023
1 parent e3ba1ca commit ec3441e
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 171 deletions.
32 changes: 30 additions & 2 deletions cmd/kaspawallet/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ type sendConfig struct {
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to"`
ToAddress string `long:"to-address" short:"t" description:"The public address to send Kaspa to" required:"true"`
FromAddresses []string `long:"from-address" short:"a" description:"Specific public address to send Kaspa from. Use multiple times to accept several addresses" required:"false"`
SendAmount float64 `long:"send-amount" short:"v" description:"An amount to send in Kaspa (e.g. 1234.12345678)" required:"true"`
SendAmount float64 `long:"send-amount" short:"v" description:"An amount to send in Kaspa (e.g. 1234.12345678)"`
IsSendAll bool `long:"send-all" description:"Send all the Kaspa in the wallet (mutually exclusive with --send-amount)"`
UseExistingChangeAddress bool `long:"use-existing-change-address" short:"u" description:"Will use an existing change address (in case no change address was ever used, it will use a new one)"`
Verbose bool `long:"show-serialized" short:"s" description:"Show a list of hex encoded sent transactions"`
config.NetworkFlags
Expand All @@ -73,7 +74,8 @@ type createUnsignedTransactionConfig struct {
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to"`
ToAddress string `long:"to-address" short:"t" description:"The public address to send Kaspa to" required:"true"`
FromAddresses []string `long:"from-address" short:"a" description:"Specific public address to send Kaspa from. Use multiple times to accept several addresses" required:"false"`
SendAmount float64 `long:"send-amount" short:"v" description:"An amount to send in Kaspa (e.g. 1234.12345678)" required:"true"`
SendAmount float64 `long:"send-amount" short:"v" description:"An amount to send in Kaspa (e.g. 1234.12345678)"`
IsSendAll bool `long:"send-all" description:"Send all the Kaspa in the wallet (mutually exclusive with --send-amount)"`
UseExistingChangeAddress bool `long:"use-existing-change-address" short:"u" description:"Will use an existing change address (in case no change address was ever used, it will use a new one)"`
config.NetworkFlags
}
Expand Down Expand Up @@ -216,6 +218,10 @@ func parseCommandLine() (subCommand string, config interface{}) {
if err != nil {
printErrorAndExit(err)
}
err = validateSendConfig(sendConf)
if err != nil {
printErrorAndExit(err)
}
config = sendConf
case sweepSubCmd:
combineNetworkFlags(&sweepConf.NetworkFlags, &cfg.NetworkFlags)
Expand All @@ -230,6 +236,10 @@ func parseCommandLine() (subCommand string, config interface{}) {
if err != nil {
printErrorAndExit(err)
}
err = validateCreateUnsignedTransactionConf(createUnsignedTransactionConf)
if err != nil {
printErrorAndExit(err)
}
config = createUnsignedTransactionConf
case signSubCmd:
combineNetworkFlags(&signConf.NetworkFlags, &cfg.NetworkFlags)
Expand Down Expand Up @@ -285,6 +295,24 @@ func parseCommandLine() (subCommand string, config interface{}) {
return parser.Command.Active.Name, config
}

func validateCreateUnsignedTransactionConf(conf *createUnsignedTransactionConfig) error {
if (!conf.IsSendAll && conf.SendAmount == 0) ||
(conf.IsSendAll && conf.SendAmount > 0) {

return errors.New("exactly one of '--send-amount' or '--all' must be specified")
}
return nil
}

func validateSendConfig(conf *sendConfig) error {
if (!conf.IsSendAll && conf.SendAmount == 0) ||
(conf.IsSendAll && conf.SendAmount > 0) {

return errors.New("exactly one of '--send-amount' or '--all' must be specified")
}
return nil
}

func combineNetworkFlags(dst, src *config.NetworkFlags) {
dst.Testnet = dst.Testnet || src.Testnet
dst.Simnet = dst.Simnet || src.Simnet
Expand Down
1 change: 1 addition & 0 deletions cmd/kaspawallet/create_unsigned_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func createUnsignedTransaction(conf *createUnsignedTransactionConfig) error {
From: conf.FromAddresses,
Address: conf.ToAddress,
Amount: sendAmountSompi,
IsSendAll: conf.IsSendAll,
UseExistingChangeAddress: conf.UseExistingChangeAddress,
})
if err != nil {
Expand Down
318 changes: 169 additions & 149 deletions cmd/kaspawallet/daemon/pb/kaspawalletd.pb.go

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions cmd/kaspawallet/daemon/pb/kaspawalletd.proto
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ message CreateUnsignedTransactionsRequest {
uint64 amount = 2;
repeated string from = 3;
bool useExistingChangeAddress = 4;
bool isSendAll = 5;
}

message CreateUnsignedTransactionsResponse {
Expand Down Expand Up @@ -109,6 +110,7 @@ message SendRequest{
string password = 3;
repeated string from = 4;
bool useExistingChangeAddress = 5;
bool isSendAll = 6;
}

message SendResponse{
Expand Down
4 changes: 0 additions & 4 deletions cmd/kaspawallet/daemon/pb/kaspawalletd_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 21 additions & 12 deletions cmd/kaspawallet/daemon/server/create_unsigned_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,16 @@ func (s *server) CreateUnsignedTransactions(_ context.Context, request *pb.Creat
s.lock.Lock()
defer s.lock.Unlock()

unsignedTransactions, err := s.createUnsignedTransactions(request.Address, request.Amount, request.From, request.UseExistingChangeAddress)
unsignedTransactions, err := s.createUnsignedTransactions(request.Address, request.Amount, request.IsSendAll,
request.From, request.UseExistingChangeAddress)
if err != nil {
return nil, err
}

return &pb.CreateUnsignedTransactionsResponse{UnsignedTransactions: unsignedTransactions}, nil
}

func (s *server) createUnsignedTransactions(address string, amount uint64, fromAddressesString []string, useExistingChangeAddress bool) ([][]byte, error) {
func (s *server) createUnsignedTransactions(address string, amount uint64, isSendAll bool, fromAddressesString []string, useExistingChangeAddress bool) ([][]byte, error) {
if !s.isSynced() {
return nil, errors.Errorf("wallet daemon is not synced yet, %s", s.formatSyncStateReport())
}
Expand All @@ -56,7 +57,7 @@ func (s *server) createUnsignedTransactions(address string, amount uint64, fromA
fromAddresses = append(fromAddresses, fromAddress)
}

selectedUTXOs, changeSompi, err := s.selectUTXOs(amount, feePerInput, fromAddresses)
selectedUTXOs, spendValue, changeSompi, err := s.selectUTXOs(amount, isSendAll, feePerInput, fromAddresses)
if err != nil {
return nil, err
}
Expand All @@ -68,7 +69,7 @@ func (s *server) createUnsignedTransactions(address string, amount uint64, fromA

payments := []*libkaspawallet.Payment{{
Address: toAddress,
Amount: amount,
Amount: spendValue,
}}
if changeSompi > 0 {
payments = append(payments, &libkaspawallet.Payment{
Expand All @@ -90,15 +91,15 @@ func (s *server) createUnsignedTransactions(address string, amount uint64, fromA
return unsignedTransactions, nil
}

func (s *server) selectUTXOs(spendAmount uint64, feePerInput uint64, fromAddresses []*walletAddress) (
selectedUTXOs []*libkaspawallet.UTXO, changeSompi uint64, err error,
) {
func (s *server) selectUTXOs(spendAmount uint64, isSendAll bool, feePerInput uint64, fromAddresses []*walletAddress) (
selectedUTXOs []*libkaspawallet.UTXO, totalReceived uint64, changeSompi uint64, err error) {

selectedUTXOs = []*libkaspawallet.UTXO{}
totalValue := uint64(0)

dagInfo, err := s.rpcClient.GetBlockDAGInfo()
if err != nil {
return nil, 0, err
return nil, 0, 0, err
}

for _, utxo := range s.utxosSortedByAmount {
Expand All @@ -120,21 +121,29 @@ func (s *server) selectUTXOs(spendAmount uint64, feePerInput uint64, fromAddress
UTXOEntry: utxo.UTXOEntry,
DerivationPath: s.walletAddressPath(utxo.address),
})

totalValue += utxo.UTXOEntry.Amount()

fee := feePerInput * uint64(len(selectedUTXOs))
totalSpend := spendAmount + fee
if totalValue >= totalSpend {
if !isSendAll && totalValue >= totalSpend {
break
}
}

fee := feePerInput * uint64(len(selectedUTXOs))
totalSpend := spendAmount + fee
var totalSpend uint64
if isSendAll {
totalSpend = totalValue
totalReceived = totalValue - fee
} else {
totalSpend = spendAmount + fee
totalReceived = spendAmount
}
if totalValue < totalSpend {
return nil, 0, errors.Errorf("Insufficient funds for send: %f required, while only %f available",
return nil, 0, 0, errors.Errorf("Insufficient funds for send: %f required, while only %f available",
float64(totalSpend)/constants.SompiPerKaspa, float64(totalValue)/constants.SompiPerKaspa)
}

return selectedUTXOs, totalValue - totalSpend, nil
return selectedUTXOs, totalReceived, totalValue - totalSpend, nil
}
4 changes: 3 additions & 1 deletion cmd/kaspawallet/daemon/server/send.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ func (s *server) Send(_ context.Context, request *pb.SendRequest) (*pb.SendRespo
s.lock.Lock()
defer s.lock.Unlock()

unsignedTransactions, err := s.createUnsignedTransactions(request.ToAddress, request.Amount, request.From, request.UseExistingChangeAddress)
unsignedTransactions, err := s.createUnsignedTransactions(request.ToAddress, request.Amount, request.IsSendAll,
request.From, request.UseExistingChangeAddress)

if err != nil {
return nil, err
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion cmd/kaspawallet/send.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,17 @@ func send(conf *sendConfig) error {
ctx, cancel := context.WithTimeout(context.Background(), daemonTimeout)
defer cancel()

sendAmountSompi := uint64(conf.SendAmount * constants.SompiPerKaspa)
var sendAmountSompi uint64
if !conf.IsSendAll {
sendAmountSompi = uint64(conf.SendAmount * constants.SompiPerKaspa)
}

createUnsignedTransactionsResponse, err :=
daemonClient.CreateUnsignedTransactions(ctx, &pb.CreateUnsignedTransactionsRequest{
From: conf.FromAddresses,
Address: conf.ToAddress,
Amount: sendAmountSompi,
IsSendAll: conf.IsSendAll,
UseExistingChangeAddress: conf.UseExistingChangeAddress,
})
if err != nil {
Expand Down

0 comments on commit ec3441e

Please sign in to comment.