github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/stellar/send.go (about)

     1  package stellar
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/keybase/client/go/libkb"
     9  	"github.com/keybase/client/go/protocol/stellar1"
    10  	"github.com/keybase/client/go/stellar/stellarcommon"
    11  	"github.com/keybase/stellarnet"
    12  )
    13  
    14  func SendPaymentLocal(mctx libkb.MetaContext, arg stellar1.SendPaymentLocalArg) (res stellar1.SendPaymentResLocal, err error) {
    15  	if arg.Bid.IsNil() && !arg.BypassBid {
    16  		return res, fmt.Errorf("missing payment ID")
    17  	}
    18  
    19  	if !arg.Bid.IsNil() {
    20  		// Finalize the payment way up here so that it's predicatble
    21  		// that when an error is returned the payment has been canceled.
    22  		data, err := getGlobal(mctx.G()).finalizeBuildPayment(mctx, arg.Bid)
    23  		if err != nil {
    24  			return res, err
    25  		}
    26  		if data == nil {
    27  			// Not expected.
    28  			return res, fmt.Errorf("the payment to send was not found")
    29  		}
    30  		mctx.Debug("got state readyToReview:%v readyToSend:%v set:%v",
    31  			data.ReadyToReview, data.ReadyToSend, data.Frozen != nil)
    32  		if arg.BypassReview {
    33  			// Pretend that a review occurred and succeeded.
    34  			// Mutating this without the DataLock is not great, but nothing
    35  			// should access this `data` ever again, so should be safe.
    36  			data.ReadyToSend = data.ReadyToSend || data.ReadyToReview
    37  		}
    38  		err = data.CheckReadyToSend(arg)
    39  		if err != nil {
    40  			return res, err
    41  		}
    42  	}
    43  
    44  	if len(arg.From) == 0 {
    45  		return res, fmt.Errorf("missing from account ID parameter")
    46  	}
    47  
    48  	to := arg.To
    49  	if arg.ToIsAccountID {
    50  		toAccountID, err := libkb.ParseStellarAccountID(arg.To)
    51  		if err != nil {
    52  			if verr, ok := err.(libkb.VerboseError); ok {
    53  				mctx.Debug(verr.Verbose())
    54  			}
    55  			return res, fmt.Errorf("recipient: %v", err)
    56  		}
    57  		to = toAccountID.String()
    58  	}
    59  
    60  	if !arg.Asset.IsNativeXLM() {
    61  		return res, fmt.Errorf("sending non-XLM assets is not supported")
    62  	}
    63  
    64  	var displayBalance DisplayBalance
    65  	if arg.WorthAmount != "" {
    66  		if arg.WorthCurrency == nil {
    67  			return res, fmt.Errorf("missing worth currency")
    68  		}
    69  		displayBalance = DisplayBalance{
    70  			Amount:   arg.WorthAmount,
    71  			Currency: arg.WorthCurrency.String(),
    72  		}
    73  	}
    74  
    75  	var cancel func()
    76  	mctx, cancel = mctx.WithTimeout(30 * time.Second)
    77  	defer cancel()
    78  
    79  	var pubMemo *stellarnet.Memo
    80  	if arg.PublicMemo != "" {
    81  		pubMemo = stellarnet.NewMemoText(arg.PublicMemo)
    82  	}
    83  
    84  	sendRes, err := SendPaymentGUI(mctx, getGlobal(mctx.G()).walletState, SendPaymentArg{
    85  		From:           arg.From,
    86  		To:             stellarcommon.RecipientInput(to),
    87  		Amount:         arg.Amount,
    88  		DisplayBalance: displayBalance,
    89  		SecretNote:     arg.SecretNote,
    90  		PublicMemo:     pubMemo,
    91  		ForceRelay:     false,
    92  		QuickReturn:    arg.QuickReturn,
    93  	})
    94  	if err != nil {
    95  		if isTimeoutError(err) {
    96  			return res, fmt.Errorf("Timed out while sending payment. Look at your payment history and maybe try again.")
    97  		}
    98  		return res, err
    99  	}
   100  	return stellar1.SendPaymentResLocal{
   101  		KbTxID:     sendRes.KbTxID,
   102  		Pending:    sendRes.Pending,
   103  		JumpToChat: sendRes.JumpToChat,
   104  	}, nil
   105  }
   106  
   107  func isTimeoutError(err error) bool {
   108  	if err == nil {
   109  		return false
   110  	}
   111  	if err == context.DeadlineExceeded {
   112  		return true
   113  	}
   114  	if err, ok := err.(libkb.APINetError); ok && err.Err == context.DeadlineExceeded {
   115  		return true
   116  	}
   117  	return false
   118  }