Compare commits

...

2 Commits

Author SHA1 Message Date
fe534b1181
polished the withdraw mpesa flow to correctly work with the selected voucher
Some checks failed
release / docker (push) Has been cancelled
2026-02-16 14:28:30 +03:00
a78639799d
add a function for ReadSwapToVoucher 2026-02-16 14:14:50 +03:00
2 changed files with 83 additions and 26 deletions

View File

@ -50,7 +50,7 @@ func (h *MenuHandlers) GetMpesaMaxLimit(ctx context.Context, sym string, input [
return res, nil
}
// Store the active transaction voucher data
// Store the active transaction voucher data (from token)
if err := store.StoreTemporaryVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil {
logg.ErrorCtxf(ctx, "failed on StoreTemporaryVoucher", "error", err)
return res, err
@ -93,7 +93,7 @@ func (h *MenuHandlers) GetMpesaMaxLimit(ctx context.Context, sym string, input [
return res, err
}
// fetch data for verification
// fetch data for verification (to_voucher data)
recipientActiveSym, recipientActiveAddress, recipientActiveDecimal, err := h.getRecipientData(ctx, string(recipientPhoneNumber))
if err != nil {
return res, err
@ -103,7 +103,7 @@ func (h *MenuHandlers) GetMpesaMaxLimit(ctx context.Context, sym string, input [
minksh := fmt.Sprintf("%f", config.MinMpesaWithdrawAmount())
minKshFormatted, _ := store.TruncateDecimalString(minksh, 0)
// If RAT is the same as SAT, return early with KSH format
// If SAT is the same as RAT, return early with KSH format
if string(metadata.TokenAddress) == string(recipientActiveAddress) {
txType = "normal"
// Save the transaction type
@ -132,7 +132,7 @@ func (h *MenuHandlers) GetMpesaMaxLimit(ctx context.Context, sym string, input [
return res, err
}
// Check if sender token is swappable
// Check if selected token is swappable
canSwap, err := h.accountService.CheckTokenInPool(ctx, string(activePoolAddress), string(metadata.TokenAddress))
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
@ -140,7 +140,7 @@ func (h *MenuHandlers) GetMpesaMaxLimit(ctx context.Context, sym string, input [
return res, nil
}
if !canSwap.CanSwapFrom { // pool issue (TODO on vis)
if !canSwap.CanSwapFrom { // pool issue (CATCH on .vis)
res.FlagSet = append(res.FlagSet, flag_incorrect_pool)
return res, nil
}
@ -175,7 +175,7 @@ func (h *MenuHandlers) GetMpesaMaxLimit(ctx context.Context, sym string, input [
return res, err
}
// save swap related data for the swap preview
// save swap related data for the swap preview (the swap to)
swapMetadata := &dataserviceapi.TokenHoldings{
TokenAddress: string(recipientActiveAddress),
TokenSymbol: string(recipientActiveSym),
@ -253,7 +253,8 @@ func (h *MenuHandlers) GetMpesaPreview(ctx context.Context, sym string, input []
// divide by the buy rate
inputAmount := kshAmount / rates.Buy
swapData, err := store.ReadSwapPreviewData(ctx, userStore, sessionId)
// Resolve active pool
activePoolAddress, _, err := h.resolveActivePoolDetails(ctx, sessionId)
if err != nil {
return res, err
}
@ -270,13 +271,13 @@ func (h *MenuHandlers) GetMpesaPreview(ctx context.Context, sym string, input []
return res, err
}
if string(transactionType) == "normal" {
// get the max based on the selected voucher balance
maxValue, err := strconv.ParseFloat(mpesaWithdrawalVoucher.Balance, 64)
if err != nil {
logg.ErrorCtxf(ctx, "Failed to convert the swapMaxAmount to a float", "error", err)
logg.ErrorCtxf(ctx, "Failed to convert the stored balance string to a float", "error", err)
return res, err
}
if string(transactionType) == "normal" {
if inputAmount > maxValue {
res.FlagSet = append(res.FlagSet, flag_invalid_amount)
res.Content = inputStr
@ -302,8 +303,20 @@ func (h *MenuHandlers) GetMpesaPreview(ctx context.Context, sym string, input []
return res, nil
}
swapToVoucher, err := store.ReadSwapToVoucher(ctx, h.userdataStore, sessionId)
if err != nil {
logg.ErrorCtxf(ctx, "failed on ReadSwapFromVoucher", "error", err)
return res, err
}
swapMaxAmount, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SWAP_MAX_AMOUNT)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read swapMaxAmount entry with", "key", storedb.DATA_ACTIVE_SWAP_MAX_AMOUNT, "error", err)
return res, err
}
// use the stored max RAT
maxRATValue, err := strconv.ParseFloat(swapData.ActiveSwapMaxAmount, 64)
maxRATValue, err := strconv.ParseFloat(string(swapMaxAmount), 64)
if err != nil {
logg.ErrorCtxf(ctx, "Failed to convert the swapMaxAmount to a float", "error", err)
return res, err
@ -315,15 +328,21 @@ func (h *MenuHandlers) GetMpesaPreview(ctx context.Context, sym string, input []
return res, nil
}
formattedAmount := fmt.Sprintf("%f", inputAmount)
// Format the amount to 2 decimal places
formattedAmount, err := store.TruncateDecimalString(fmt.Sprintf("%f", inputAmount), 2)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_invalid_amount)
res.Content = inputStr
return res, nil
}
finalAmountStr, err := store.ParseAndScaleAmount(formattedAmount, swapData.ActiveSwapToDecimal)
finalAmountStr, err := store.ParseAndScaleAmount(formattedAmount, swapToVoucher.TokenDecimals)
if err != nil {
return res, err
}
// call the credit send API to get the reverse quote
r, err := h.accountService.GetCreditSendReverseQuote(ctx, swapData.ActivePoolAddress, swapData.ActiveSwapFromAddress, swapData.ActiveSwapToAddress, finalAmountStr)
r, err := h.accountService.GetCreditSendReverseQuote(ctx, string(activePoolAddress), mpesaWithdrawalVoucher.TokenAddress, swapToVoucher.TokenAddress, finalAmountStr)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = l.Get("Your request failed. Please try again later.")
@ -349,13 +368,13 @@ func (h *MenuHandlers) GetMpesaPreview(ctx context.Context, sym string, input []
}
// covert for display
quoteInputStr := store.ScaleDownBalance(sendInputAmount, swapData.ActiveSwapFromDecimal)
quoteInputStr := store.ScaleDownBalance(sendInputAmount, mpesaWithdrawalVoucher.TokenDecimals)
// Format the quoteInputStr amount to 2 decimal places
qouteInputAmount, _ := store.TruncateDecimalString(quoteInputStr, 2)
res.Content = l.Get(
"You are sending %s %s in order to receive ~ %s ksh",
qouteInputAmount, swapData.ActiveSwapFromSym, inputStr,
qouteInputAmount, mpesaWithdrawalVoucher.TokenSymbol, inputStr,
)
return res, nil
@ -381,16 +400,12 @@ func (h *MenuHandlers) InitiateGetMpesa(ctx context.Context, sym string, input [
mpesaAddress := config.DefaultMpesaAddress()
swapData, err := store.ReadSwapPreviewData(ctx, userStore, sessionId)
if err != nil {
return res, err
}
transactionType, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_SEND_TRANSACTION_TYPE)
if err != nil {
return res, err
}
// get the selected voucher
mpesaWithdrawalVoucher, err := store.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId)
if err != nil {
logg.ErrorCtxf(ctx, "failed on GetTemporaryVoucherData", "error", err)
@ -425,6 +440,24 @@ func (h *MenuHandlers) InitiateGetMpesa(ctx context.Context, sym string, input [
return res, nil
}
publicKey, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read publicKey entry", "key", storedb.DATA_PUBLIC_KEY, "error", err)
return res, err
}
swapToVoucher, err := store.ReadSwapToVoucher(ctx, h.userdataStore, sessionId)
if err != nil {
logg.ErrorCtxf(ctx, "failed on ReadSwapFromVoucher", "error", err)
return res, err
}
// Resolve active pool
activePoolAddress, _, err := h.resolveActivePoolDetails(ctx, sessionId)
if err != nil {
return res, err
}
swapAmount, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SWAP_AMOUNT)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read swapAmount entry with", "key", storedb.DATA_ACTIVE_SWAP_AMOUNT, "error", err)
@ -434,7 +467,7 @@ func (h *MenuHandlers) InitiateGetMpesa(ctx context.Context, sym string, input [
swapAmountStr := string(swapAmount)
// Call the poolSwap API
poolSwap, err := h.accountService.PoolSwap(ctx, swapAmountStr, swapData.PublicKey, swapData.ActiveSwapFromAddress, swapData.ActivePoolAddress, swapData.ActiveSwapToAddress)
poolSwap, err := h.accountService.PoolSwap(ctx, swapAmountStr, string(publicKey), mpesaWithdrawalVoucher.TokenAddress, string(activePoolAddress), swapToVoucher.TokenAddress)
if err != nil {
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
res.FlagSet = append(res.FlagSet, flag_api_call_error)
@ -454,7 +487,7 @@ func (h *MenuHandlers) InitiateGetMpesa(ctx context.Context, sym string, input [
}
// Initiate a send to mpesa after the swap
tokenTransfer, err := h.accountService.TokenTransfer(ctx, string(amount), swapData.PublicKey, mpesaAddress, swapData.ActiveSwapToAddress)
tokenTransfer, err := h.accountService.TokenTransfer(ctx, string(amount),string(publicKey), mpesaAddress, swapToVoucher.TokenAddress)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = l.Get("Your request failed. Please try again later.")

View File

@ -236,3 +236,27 @@ func ReadSwapFromVoucher(ctx context.Context, store DataStore, sessionId string)
TokenAddress: data[storedb.DATA_ACTIVE_SWAP_FROM_ADDRESS],
}, nil
}
// ReadSwapToVoucher retrieves the swap to voucher being swapped from the pool
func ReadSwapToVoucher(ctx context.Context, store DataStore, sessionId string) (*dataserviceapi.TokenHoldings, error) {
keys := []storedb.DataTyp{
storedb.DATA_ACTIVE_SWAP_TO_SYM,
storedb.DATA_ACTIVE_SWAP_TO_DECIMAL,
storedb.DATA_ACTIVE_SWAP_TO_ADDRESS,
}
data := make(map[storedb.DataTyp]string)
for _, key := range keys {
value, err := store.ReadEntry(ctx, sessionId, key)
if err != nil {
return nil, fmt.Errorf("failed to get data key %x: %v", key, err)
}
data[key] = string(value)
}
return &dataserviceapi.TokenHoldings{
TokenSymbol: data[storedb.DATA_ACTIVE_SWAP_TO_SYM],
TokenDecimals: data[storedb.DATA_ACTIVE_SWAP_TO_DECIMAL],
TokenAddress: data[storedb.DATA_ACTIVE_SWAP_TO_ADDRESS],
}, nil
}