Compare commits

..

No commits in common. "3949959aa36e9153a98019f4ce4d816e93989a6f" and "f7de79f51aec0e2d3d792b44aed437f35b3e118c" have entirely different histories.

15 changed files with 17 additions and 499 deletions

View File

@ -3,13 +3,11 @@ package application
import ( import (
"context" "context"
"fmt" "fmt"
"strconv"
"git.defalsify.org/vise.git/db" "git.defalsify.org/vise.git/db"
"git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/resource"
"git.grassecon.net/grassrootseconomics/sarafu-vise/store" "git.grassecon.net/grassrootseconomics/sarafu-vise/store"
storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db" storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
"gopkg.in/leonelquinteros/gotext.v1" "gopkg.in/leonelquinteros/gotext.v1"
) )
@ -54,25 +52,7 @@ func (h *MenuHandlers) CheckBalance(ctx context.Context, sym string, input []byt
return res, err return res, err
} }
} }
content, err = loadUserContent(ctx, string(activeSym), string(activeBal), string(accAlias))
// get current credit
currentCredit, err := store.ReadEntry(ctx, sessionId, storedb.DATA_CURRENT_CREDIT)
if err != nil {
if !db.IsNotFound(err) {
logg.ErrorCtxf(ctx, "failed to read currentCredit entry with", "key", storedb.DATA_CURRENT_CREDIT, "error", err)
return res, err
}
}
// get current debt
currentDebt, err := store.ReadEntry(ctx, sessionId, storedb.DATA_CURRENT_DEBT)
if err != nil {
if !db.IsNotFound(err) {
logg.ErrorCtxf(ctx, "failed to read currentDebt entry with", "key", storedb.DATA_CURRENT_DEBT, "error", err)
return res, err
}
}
content, err = loadUserContent(ctx, string(activeSym), string(activeBal), string(accAlias), string(currentCredit), string(currentDebt))
if err != nil { if err != nil {
return res, err return res, err
} }
@ -82,7 +62,7 @@ func (h *MenuHandlers) CheckBalance(ctx context.Context, sym string, input []byt
} }
// loadUserContent loads the main user content in the main menu: the alias, balance and active symbol associated with active voucher // loadUserContent loads the main user content in the main menu: the alias, balance and active symbol associated with active voucher
func loadUserContent(ctx context.Context, activeSym, balance, alias, currectCredit, currentDebt string) (string, error) { func loadUserContent(ctx context.Context, activeSym string, balance string, alias string) (string, error) {
var content string var content string
code := codeFromCtx(ctx) code := codeFromCtx(ctx)
@ -95,25 +75,12 @@ func loadUserContent(ctx context.Context, activeSym, balance, alias, currectCred
formattedAmount = "0.00" formattedAmount = "0.00"
} }
formattedCredit, err := store.TruncateDecimalString(currectCredit, 0) // format the final output
if err != nil {
formattedCredit = "0"
}
formattedDebt, err := store.TruncateDecimalString(currentDebt, 0)
if err != nil {
formattedDebt = "0"
}
// format the final outputs
balStr := fmt.Sprintf("%s %s", formattedAmount, activeSym) balStr := fmt.Sprintf("%s %s", formattedAmount, activeSym)
creditStr := fmt.Sprintf("Credit: %s ksh", formattedCredit)
debtStr := fmt.Sprintf("Debt: %s ksh", formattedDebt)
if alias != "" { if alias != "" {
content = l.Get("%s\nBalance: %s\n%s\n%s", alias, balStr, creditStr, debtStr) content = l.Get("%s\nBalance: %s\n", alias, balStr)
} else { } else {
content = l.Get("Balance: %s\n%s\n%s", balStr, creditStr, debtStr) content = l.Get("Balance: %s\n", balStr)
} }
return content, nil return content, nil
} }
@ -131,131 +98,3 @@ func (h *MenuHandlers) FetchCommunityBalance(ctx context.Context, sym string, in
res.Content = l.Get("Community Balance: 0.00") res.Content = l.Get("Community Balance: 0.00")
return res, nil return res, nil
} }
// CalculateCreditAndDebt calls the API to get the credit and debt
// uses the pretium rates to convert the value to Ksh
func (h *MenuHandlers) CalculateCreditAndDebt(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
userStore := h.userdataStore
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
// Fetch session data
_, _, activeSym, _, publicKey, _, err := h.getSessionData(ctx, sessionId)
if err != nil {
return res, err
}
// Get active pool address and symbol or fall back to default
activePoolAddress, _, err := h.resolveActivePoolDetails(ctx, sessionId)
if err != nil {
return res, err
}
// call the api using the activePoolAddress to get a list of SwapToSymbolsData
swappableVouchers, err := h.accountService.GetPoolSwappableFromVouchers(ctx, string(activePoolAddress), string(publicKey))
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
logg.ErrorCtxf(ctx, "failed on GetPoolSwappableFromVouchers", "error", err)
return res, err
}
logg.InfoCtxf(ctx, "GetPoolSwappableFromVouchers", "swappable vouchers", swappableVouchers)
// Return if there are no vouchers
if len(swappableVouchers) == 0 {
return res, nil
}
// Filter out the active voucher from swappableVouchers
filteredSwappableVouchers := make([]dataserviceapi.TokenHoldings, 0, len(swappableVouchers))
for _, s := range swappableVouchers {
if s.TokenSymbol != string(activeSym) {
filteredSwappableVouchers = append(filteredSwappableVouchers, s)
}
}
// Store as filtered swap to list data (excluding the current active voucher) for future reference
data := store.ProcessVouchers(filteredSwappableVouchers)
logg.InfoCtxf(ctx, "ProcessVouchers", "data", data)
// Find the matching voucher data
activeSymStr := string(activeSym)
var activeData *dataserviceapi.TokenHoldings
for _, voucher := range swappableVouchers {
if voucher.TokenSymbol == activeSymStr {
activeData = &voucher
break
}
}
if activeData == nil {
logg.InfoCtxf(ctx, "activeSym not found in vouchers, returning 0", "activeSym", activeSymStr)
return res, nil
}
// Scale down the active balance (credit)
// Max swappable value from pool using the active token
scaledCredit := store.ScaleDownBalance(activeData.Balance, activeData.TokenDecimals)
// Calculate total debt (sum of other vouchers)
scaledDebt := "0"
for _, voucher := range swappableVouchers {
// Skip the active token
if voucher.TokenSymbol == activeSymStr {
continue
}
scaled := store.ScaleDownBalance(voucher.Balance, voucher.TokenDecimals)
// Add scaled balances (decimal-safe)
scaledDebt = store.AddDecimalStrings(scaledDebt, scaled)
}
// call the mpesa rates API to get the rates
rates, err := h.accountService.GetMpesaOnrampRates(ctx)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = l.Get("Your request failed. Please try again later.")
logg.ErrorCtxf(ctx, "failed on GetMpesaOnrampRates", "error", err)
return res, nil
}
creditFloat, _ := strconv.ParseFloat(scaledCredit, 64)
creditksh := fmt.Sprintf("%f", creditFloat*rates.Buy)
kshFormattedCredit, _ := store.TruncateDecimalString(creditksh, 0)
debtFloat, _ := strconv.ParseFloat(scaledDebt, 64)
debtksh := fmt.Sprintf("%f", debtFloat*rates.Buy)
kshFormattedDebt, _ := store.TruncateDecimalString(debtksh, 0)
dataMap := map[storedb.DataTyp]string{
storedb.DATA_POOL_TO_SYMBOLS: data.Symbols,
storedb.DATA_POOL_TO_BALANCES: data.Balances,
storedb.DATA_POOL_TO_DECIMALS: data.Decimals,
storedb.DATA_POOL_TO_ADDRESSES: data.Addresses,
storedb.DATA_CURRENT_CREDIT: kshFormattedCredit,
storedb.DATA_CURRENT_DEBT: kshFormattedDebt,
}
// Write data entries
for key, value := range dataMap {
if err := userStore.WriteEntry(ctx, sessionId, key, []byte(value)); err != nil {
logg.ErrorCtxf(ctx, "Failed to write data entry for sessionId: %s", sessionId, "key", key, "error", err)
continue
}
}
return res, nil
}

View File

@ -1,262 +0,0 @@
package application
import (
"context"
"fmt"
"strconv"
"git.defalsify.org/vise.git/resource"
"git.grassecon.net/grassrootseconomics/sarafu-vise/store"
storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db"
"gopkg.in/leonelquinteros/gotext.v1"
)
// CalculateMaxPayDebt calculates the max debt removal
func (h *MenuHandlers) CalculateMaxPayDebt(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
flag_low_swap_amount, _ := h.flagManager.GetFlag("flag_low_swap_amount")
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
inputStr := string(input)
if inputStr == "0" || inputStr == "9" {
return res, nil
}
userStore := h.userdataStore
// Resolve active pool
_, activePoolName, err := h.resolveActivePoolDetails(ctx, sessionId)
if err != nil {
return res, err
}
metadata, err := store.GetSwapToVoucherData(ctx, userStore, sessionId, "1")
if err != nil {
return res, fmt.Errorf("failed to retrieve swap to voucher data: %v", err)
}
if metadata == nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
return res, nil
}
logg.InfoCtxf(ctx, "Metadata from GetSwapToVoucherData:", "metadata", metadata)
// Store the active swap_to data
if err := store.UpdateSwapToVoucherData(ctx, userStore, sessionId, metadata); err != nil {
logg.ErrorCtxf(ctx, "failed on UpdateSwapToVoucherData", "error", err)
return res, err
}
swapData, err := store.ReadSwapData(ctx, userStore, sessionId)
if err != nil {
return res, err
}
// call the api using the ActivePoolAddress, ActiveSwapToAddress as the from (FT), ActiveSwapFromAddress as the to (AT) and PublicKey to get the swap max limit
r, err := h.accountService.GetSwapFromTokenMaxLimit(ctx, swapData.ActivePoolAddress, swapData.ActiveSwapToAddress, swapData.ActiveSwapFromAddress, swapData.PublicKey)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
logg.ErrorCtxf(ctx, "failed on GetSwapFromTokenMaxLimit", "error", err)
return res, nil
}
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(r.Max))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write full max amount entry with", "key", storedb.DATA_TEMPORARY_VALUE, "value", r.Max, "error", err)
return res, err
}
// Scale down the amount
maxAmountStr := store.ScaleDownBalance(r.Max, swapData.ActiveSwapToDecimal)
if err != nil {
return res, err
}
maxAmountFloat, err := strconv.ParseFloat(maxAmountStr, 64)
if err != nil {
logg.ErrorCtxf(ctx, "failed to parse maxAmountStr as float", "value", maxAmountStr, "error", err)
return res, err
}
// Format to 2 decimal places
maxStr, _ := store.TruncateDecimalString(string(maxAmountStr), 2)
if maxAmountFloat < 0.1 {
// return with low amount flag
res.Content = maxStr
res.FlagSet = append(res.FlagSet, flag_low_swap_amount)
return res, nil
}
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SWAP_MAX_AMOUNT, []byte(maxStr))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write swap max amount entry with", "key", storedb.DATA_ACTIVE_SWAP_MAX_AMOUNT, "value", maxStr, "error", err)
return res, err
}
res.Content = l.Get(
"You can remove a maximum of %s %s from '%s'\n\nEnter amount of %s:",
maxStr,
swapData.ActiveSwapToSym,
string(activePoolName),
swapData.ActiveSwapToSym,
)
return res, nil
}
// ConfirmDebtRemoval displays the debt preview for a confirmation
func (h *MenuHandlers) ConfirmDebtRemoval(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
inputStr := string(input)
if inputStr == "0" {
return res, nil
}
flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount")
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
userStore := h.userdataStore
swapData, err := store.ReadSwapPreviewData(ctx, userStore, sessionId)
if err != nil {
return res, err
}
maxValue, err := strconv.ParseFloat(swapData.ActiveSwapMaxAmount, 64)
if err != nil {
logg.ErrorCtxf(ctx, "Failed to convert the swapMaxAmount to a float", "error", err)
return res, err
}
inputAmount, err := strconv.ParseFloat(inputStr, 64)
if err != nil || inputAmount > maxValue {
res.FlagSet = append(res.FlagSet, flag_invalid_amount)
res.Content = inputStr
return res, nil
}
storedMax, _ := userStore.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE)
var finalAmountStr string
if inputStr == swapData.ActiveSwapMaxAmount {
finalAmountStr = string(storedMax)
} else {
finalAmountStr, err = store.ParseAndScaleAmount(inputStr, swapData.ActiveSwapToDecimal)
if err != nil {
return res, err
}
}
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SWAP_AMOUNT, []byte(finalAmountStr))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write swap amount entry with", "key", storedb.DATA_ACTIVE_SWAP_AMOUNT, "value", finalAmountStr, "error", err)
return res, err
}
// store the user's input amount in the temporary value
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(inputStr))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write inputStr amount entry with", "key", storedb.DATA_TEMPORARY_VALUE, "value", inputStr, "error", err)
return res, err
}
// call the API to get the quote
r, err := h.accountService.GetPoolSwapQuote(ctx, finalAmountStr, swapData.PublicKey, swapData.ActiveSwapToAddress, swapData.ActivePoolAddress, swapData.ActiveSwapFromAddress)
if err != nil {
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = l.Get("Your request failed. Please try again later.")
logg.ErrorCtxf(ctx, "failed on poolSwap", "error", err)
return res, nil
}
// Scale down the quoted amount
quoteAmountStr := store.ScaleDownBalance(r.OutValue, swapData.ActiveSwapToDecimal)
// Format to 2 decimal places
qouteStr, _ := store.TruncateDecimalString(string(quoteAmountStr), 2)
res.Content = l.Get(
"Please confirm that you will use %s %s to remove your debt of %s %s\n",
inputStr, swapData.ActiveSwapToSym, qouteStr, swapData.ActiveSwapFromSym,
)
return res, nil
}
// InitiatePayDebt calls the poolSwap to swap the token for the active voucher.
func (h *MenuHandlers) InitiatePayDebt(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
var err error
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized")
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
userStore := h.userdataStore
// Resolve active pool
_, activePoolName, err := h.resolveActivePoolDetails(ctx, sessionId)
if err != nil {
return res, err
}
swapData, err := store.ReadSwapPreviewData(ctx, userStore, 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)
return res, err
}
swapAmountStr := string(swapAmount)
// Call the poolSwap API
r, err := h.accountService.PoolSwap(ctx, swapAmountStr, swapData.PublicKey, swapData.ActiveSwapToAddress, swapData.ActivePoolAddress, swapData.ActiveSwapFromAddress)
if err != nil {
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = l.Get("Your request failed. Please try again later.")
logg.ErrorCtxf(ctx, "failed on poolSwap", "error", err)
return res, nil
}
trackingId := r.TrackingId
logg.InfoCtxf(ctx, "poolSwap", "trackingId", trackingId)
res.Content = l.Get(
"Your request has been sent. You will receive an SMS when your debt of %s %s has been removed from %s.",
swapData.TemporaryValue,
swapData.ActiveSwapToSym,
activePoolName,
)
res.FlagReset = append(res.FlagReset, flag_account_authorized)
return res, nil
}

View File

@ -303,7 +303,7 @@ func (h *MenuHandlers) SwapPreview(ctx context.Context, sym string, input []byte
// store the user's input amount in the temporary value // store the user's input amount in the temporary value
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(inputStr)) err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(inputStr))
if err != nil { if err != nil {
logg.ErrorCtxf(ctx, "failed to write inputStr amount entry with", "key", storedb.DATA_TEMPORARY_VALUE, "value", inputStr, "error", err) logg.ErrorCtxf(ctx, "failed to write swap amount entry with", "key", storedb.DATA_ACTIVE_SWAP_AMOUNT, "value", finalAmountStr, "error", err)
return res, err return res, err
} }

View File

@ -81,7 +81,6 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountService)
ls.DbRs.AddLocalFunc("check_account_status", appHandlers.CheckAccountStatus) ls.DbRs.AddLocalFunc("check_account_status", appHandlers.CheckAccountStatus)
ls.DbRs.AddLocalFunc("authorize_account", appHandlers.Authorize) ls.DbRs.AddLocalFunc("authorize_account", appHandlers.Authorize)
ls.DbRs.AddLocalFunc("quit", appHandlers.Quit) ls.DbRs.AddLocalFunc("quit", appHandlers.Quit)
ls.DbRs.AddLocalFunc("calc_credit_debt", appHandlers.CalculateCreditAndDebt)
ls.DbRs.AddLocalFunc("check_balance", appHandlers.CheckBalance) ls.DbRs.AddLocalFunc("check_balance", appHandlers.CheckBalance)
ls.DbRs.AddLocalFunc("validate_recipient", appHandlers.ValidateRecipient) ls.DbRs.AddLocalFunc("validate_recipient", appHandlers.ValidateRecipient)
ls.DbRs.AddLocalFunc("transaction_reset", appHandlers.TransactionReset) ls.DbRs.AddLocalFunc("transaction_reset", appHandlers.TransactionReset)
@ -147,7 +146,7 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountService)
ls.DbRs.AddLocalFunc("send_mpesa_min_limit", appHandlers.SendMpesaMinLimit) ls.DbRs.AddLocalFunc("send_mpesa_min_limit", appHandlers.SendMpesaMinLimit)
ls.DbRs.AddLocalFunc("send_mpesa_preview", appHandlers.SendMpesaPreview) ls.DbRs.AddLocalFunc("send_mpesa_preview", appHandlers.SendMpesaPreview)
ls.DbRs.AddLocalFunc("initiate_send_mpesa", appHandlers.InitiateSendMpesa) ls.DbRs.AddLocalFunc("initiate_send_mpesa", appHandlers.InitiateSendMpesa)
ls.DbRs.AddLocalFunc("calculate_max_pay_debt", appHandlers.CalculateMaxPayDebt) ls.DbRs.AddLocalFunc("calculate_pay_debt", appHandlers.CalculatePayDebt)
ls.DbRs.AddLocalFunc("confirm_debt_removal", appHandlers.ConfirmDebtRemoval) ls.DbRs.AddLocalFunc("confirm_debt_removal", appHandlers.ConfirmDebtRemoval)
ls.DbRs.AddLocalFunc("initiate_pay_debt", appHandlers.InitiatePayDebt) ls.DbRs.AddLocalFunc("initiate_pay_debt", appHandlers.InitiatePayDebt)

View File

@ -73,8 +73,8 @@ msgstr "Utapokea kidokezo cha PIN yako ya M-Pesa hivi karibuni kutuma %s ksh na
msgid "Your request has been sent. Thank you for using Sarafu" msgid "Your request has been sent. Thank you for using Sarafu"
msgstr "Ombi lako limetumwa. Asante kwa kutumia huduma ya Sarafu" msgstr "Ombi lako limetumwa. Asante kwa kutumia huduma ya Sarafu"
msgid "You can remove a maximum of %s %s from '%s' pool\n\nEnter amount of %s:" msgid "You can remove a maximum of %s %s from %s pool\n"
msgstr "Unaweza kuondoa kiwango cha juu cha %s %s kutoka kwenye '%s'\n\nWeka kiwango cha %s:" msgstr "Unaweza kuondoa kiwango cha juu cha %s %s kutoka kwenye bwawa la %s.\n"
msgid "Please confirm that you will use %s %s to remove your debt of %s %s\n" msgid "Please confirm that you will use %s %s to remove your debt of %s %s\n"
msgstr "Tafadhali thibitisha kwamba utatumia %s %s kulipa deni lako la %s %s.\nWeka PIN yako:" msgstr "Tafadhali thibitisha kwamba utatumia %s %s kulipa deni lako la %s %s.\nWeka PIN yako:"

View File

@ -1 +0,0 @@
You have a low debt amount

View File

@ -1,5 +0,0 @@
MOUT back 0
MOUT quit 9
HALT
INCMP ^ 0
INCMP quit 9

View File

@ -1 +0,0 @@
Kiasi cha deni lako ni cha chini sana

View File

@ -3,10 +3,7 @@ RELOAD clear_temporary_value
LOAD manage_vouchers 160 LOAD manage_vouchers 160
RELOAD manage_vouchers RELOAD manage_vouchers
CATCH api_failure flag_api_call_error 1 CATCH api_failure flag_api_call_error 1
LOAD calc_credit_debt 60 LOAD check_balance 128
RELOAD calc_credit_debt
CATCH api_failure flag_api_call_error 1
LOAD check_balance 148
RELOAD check_balance RELOAD check_balance
MAP check_balance MAP check_balance
MOUT send 1 MOUT send 1

View File

@ -1 +1,2 @@
{{.calculate_max_pay_debt}} {{.calculate_pay_debt}}
Enter amount:

View File

@ -1,7 +1,6 @@
LOAD calculate_max_pay_debt 0 LOAD calculate_pay_debt 0
RELOAD calculate_max_pay_debt RELOAD calculate_pay_debt
MAP calculate_max_pay_debt MAP calculate_pay_debt
CATCH low_pay_debt_amount flag_low_swap_amount 1
MOUT back 0 MOUT back 0
HALT HALT
INCMP _ 0 INCMP _ 0

View File

@ -1 +1,2 @@
{{.calculate_max_pay_debt}} {{.calculate_pay_debt}}
Weka kiwango:

View File

@ -93,10 +93,6 @@ const (
DATA_SEND_TRANSACTION_TYPE DATA_SEND_TRANSACTION_TYPE
// Holds the recipient formatted phone number // Holds the recipient formatted phone number
DATA_RECIPIENT_PHONE_NUMBER DATA_RECIPIENT_PHONE_NUMBER
// holds the current credit of the user
DATA_CURRENT_CREDIT
// holds the current debt of the user
DATA_CURRENT_DEBT
) )
const ( const (

View File

@ -18,7 +18,6 @@ type SwapData struct {
ActiveSwapFromAddress string ActiveSwapFromAddress string
ActiveSwapToSym string ActiveSwapToSym string
ActiveSwapToAddress string ActiveSwapToAddress string
ActiveSwapToDecimal string
} }
type SwapPreviewData struct { type SwapPreviewData struct {
@ -44,7 +43,6 @@ func ReadSwapData(ctx context.Context, store DataStore, sessionId string) (SwapD
"ActiveSwapFromAddress": storedb.DATA_ACTIVE_ADDRESS, "ActiveSwapFromAddress": storedb.DATA_ACTIVE_ADDRESS,
"ActiveSwapToSym": storedb.DATA_ACTIVE_SWAP_TO_SYM, "ActiveSwapToSym": storedb.DATA_ACTIVE_SWAP_TO_SYM,
"ActiveSwapToAddress": storedb.DATA_ACTIVE_SWAP_TO_ADDRESS, "ActiveSwapToAddress": storedb.DATA_ACTIVE_SWAP_TO_ADDRESS,
"ActiveSwapToDecimal": storedb.DATA_ACTIVE_SWAP_TO_DECIMAL,
} }
v := reflect.ValueOf(&data).Elem() v := reflect.ValueOf(&data).Elem()

View File

@ -225,46 +225,3 @@ func FormatVoucherList(ctx context.Context, symbolsData, balancesData string) []
} }
return combined return combined
} }
// AddDecimalStrings adds two decimal numbers represented as strings
// and returns the result as a string without losing precision.
func AddDecimalStrings(a, b string) string {
x, ok := new(big.Rat).SetString(a)
if !ok {
x = new(big.Rat)
}
y, ok := new(big.Rat).SetString(b)
if !ok {
y = new(big.Rat)
}
x.Add(x, y)
// Convert back to string without scientific notation
return x.FloatString(maxDecimalPlaces(x, y))
}
// maxDecimalPlaces ensures we preserve enough decimal precision
func maxDecimalPlaces(rats ...*big.Rat) int {
max := 0
for _, r := range rats {
if r == nil {
continue
}
if d := decimalPlaces(r); d > max {
max = d
}
}
return max
}
func decimalPlaces(r *big.Rat) int {
s := r.FloatString(18)
for i := len(s) - 1; i >= 0; i-- {
if s[i] == '.' {
return len(s) - i - 1
}
}
return 0
}