Compare commits

..

No commits in common. "master" and "v1.4.2-rc.4" have entirely different histories.

27 changed files with 76 additions and 180 deletions

2
go.mod
View File

@ -5,7 +5,7 @@ go 1.23.4
require ( require (
git.defalsify.org/vise.git v0.3.2-0.20250528124150-03bf7bfc1b66 git.defalsify.org/vise.git v0.3.2-0.20250528124150-03bf7bfc1b66
git.grassecon.net/grassrootseconomics/common v0.9.0-beta.1.0.20250417111317-2953f4c2f32e git.grassecon.net/grassrootseconomics/common v0.9.0-beta.1.0.20250417111317-2953f4c2f32e
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251028083421-fe897cca84f2 git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251022084613-532547899f63
git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306 git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306
git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694 git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694
github.com/alecthomas/assert/v2 v2.2.2 github.com/alecthomas/assert/v2 v2.2.2

4
go.sum
View File

@ -24,10 +24,6 @@ git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251021120522-
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251021120522-6f7802b58cf5/go.mod h1:y/vsN8UO0wSxZk3gv0y5df4RPKMJI6TIxjVcVCPF8T8= git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251021120522-6f7802b58cf5/go.mod h1:y/vsN8UO0wSxZk3gv0y5df4RPKMJI6TIxjVcVCPF8T8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251022084613-532547899f63 h1:yznaUXeAy+qiZb2nCxosYXE5HyCPpynIoplEuYV/zQM= git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251022084613-532547899f63 h1:yznaUXeAy+qiZb2nCxosYXE5HyCPpynIoplEuYV/zQM=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251022084613-532547899f63/go.mod h1:y/vsN8UO0wSxZk3gv0y5df4RPKMJI6TIxjVcVCPF8T8= git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251022084613-532547899f63/go.mod h1:y/vsN8UO0wSxZk3gv0y5df4RPKMJI6TIxjVcVCPF8T8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251028081048-a705443786fd h1:VIj5OdRae2wfE6NdLp6ZdHv0jtRbOeRURYQCU29RWBM=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251028081048-a705443786fd/go.mod h1:y/vsN8UO0wSxZk3gv0y5df4RPKMJI6TIxjVcVCPF8T8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251028083421-fe897cca84f2 h1:wf//obTSLW5VZ0gM25l0U5oV/d+TBXX+1ClSMkEU7Uc=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251028083421-fe897cca84f2/go.mod h1:y/vsN8UO0wSxZk3gv0y5df4RPKMJI6TIxjVcVCPF8T8=
git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306 h1:Jo+yWysWw/N5BJQtAyEMN8ePVvAyPHv+JG4lQti+1N4= git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306 h1:Jo+yWysWw/N5BJQtAyEMN8ePVvAyPHv+JG4lQti+1N4=
git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306/go.mod h1:FdLwYtzsjOIcDiW4uDgDYnB4Wdzq12uJUe0QHSSPbSo= git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306/go.mod h1:FdLwYtzsjOIcDiW4uDgDYnB4Wdzq12uJUe0QHSSPbSo=
git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694 h1:DjJlBSz0S13acft5XZDWk7ZYnzElym0xLMYEVgyNJ+E= git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694 h1:DjJlBSz0S13acft5XZDWk7ZYnzElym0xLMYEVgyNJ+E=

View File

@ -294,14 +294,13 @@ func (h *MenuHandlers) ResetTransactionAmount(ctx context.Context, sym string, i
} }
flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount") flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount")
flag_swap_transaction, _ := h.flagManager.GetFlag("flag_swap_transaction")
store := h.userdataStore store := h.userdataStore
err = store.WriteEntry(ctx, sessionId, storedb.DATA_AMOUNT, []byte("")) err = store.WriteEntry(ctx, sessionId, storedb.DATA_AMOUNT, []byte(""))
if err != nil { if err != nil {
return res, nil return res, nil
} }
res.FlagReset = append(res.FlagReset, flag_invalid_amount, flag_swap_transaction) res.FlagReset = append(res.FlagReset, flag_invalid_amount)
return res, nil return res, nil
} }
@ -322,10 +321,6 @@ func (h *MenuHandlers) MaxAmount(ctx context.Context, sym string, input []byte)
flag_swap_transaction, _ := h.flagManager.GetFlag("flag_swap_transaction") flag_swap_transaction, _ := h.flagManager.GetFlag("flag_swap_transaction")
userStore := h.userdataStore userStore := h.userdataStore
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
// Fetch session data // Fetch session data
transactionType, activeBal, activeSym, activeAddress, publicKey, activeDecimal, err := h.getSessionData(ctx, sessionId) transactionType, activeBal, activeSym, activeAddress, publicKey, activeDecimal, err := h.getSessionData(ctx, sessionId)
if err != nil { if err != nil {
@ -335,11 +330,10 @@ func (h *MenuHandlers) MaxAmount(ctx context.Context, sym string, input []byte)
// Format the active balance amount to 2 decimal places // Format the active balance amount to 2 decimal places
formattedBalance, _ := store.TruncateDecimalString(string(activeBal), 2) formattedBalance, _ := store.TruncateDecimalString(string(activeBal), 2)
// If normal transaction, or if the sym is max_amount, return balance // If normal transaction, return balance
if string(transactionType) == "normal" || sym == "max_amount" { if string(transactionType) == "normal" {
res.FlagReset = append(res.FlagReset, flag_swap_transaction) res.FlagReset = append(res.FlagReset, flag_swap_transaction)
res.Content = fmt.Sprintf("%s %s", formattedBalance, string(activeSym))
res.Content = l.Get("Maximum amount: %s %s\nEnter amount:", formattedBalance, string(activeSym))
return res, nil return res, nil
} }
@ -369,38 +363,37 @@ func (h *MenuHandlers) MaxAmount(ctx context.Context, sym string, input []byte)
res.FlagSet = append(res.FlagSet, flag_api_error) res.FlagSet = append(res.FlagSet, flag_api_error)
logg.ErrorCtxf(ctx, "failed on CheckTokenInPool", "error", err) logg.ErrorCtxf(ctx, "failed on CheckTokenInPool", "error", err)
} }
res.FlagReset = append(res.FlagReset, flag_swap_transaction) res.Content = fmt.Sprintf("%s %s", formattedBalance, string(activeSym))
res.Content = l.Get("Maximum amount: %s %s\nEnter amount:", formattedBalance, string(activeSym)) return res, err
return res, nil
} }
// retrieve the max credit send amounts // Calculate max swappable amount
maxSAT, maxRAT, err := h.calculateSendCreditLimits(ctx, activePoolAddress, activeAddress, recipientActiveAddress, publicKey, activeDecimal, recipientActiveDecimal) maxStr, err := h.calculateSwapMaxAmount(ctx, activePoolAddress, activeAddress, recipientActiveAddress, publicKey, activeDecimal)
if err != nil { if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_error) res.FlagSet = append(res.FlagSet, flag_api_error)
return res, err return res, err
} }
// Fallback if below minimum // Fallback if below minimum
maxFloat, _ := strconv.ParseFloat(maxSAT, 64) maxFloat, _ := strconv.ParseFloat(maxStr, 64)
if maxFloat < 0.1 { if maxFloat < 0.1 {
res.FlagReset = append(res.FlagReset, flag_swap_transaction) res.Content = fmt.Sprintf("%s %s", formattedBalance, string(activeSym))
res.Content = l.Get("Maximum amount: %s %s\nEnter amount:", formattedBalance, string(activeSym))
return res, nil return res, nil
} }
// Save max RAT amount to be used in validating the user's input // Save max swap amount and return
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SWAP_MAX_AMOUNT, []byte(maxRAT)) err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SWAP_MAX_AMOUNT, []byte(maxStr))
if err != nil { if err != nil {
logg.ErrorCtxf(ctx, "failed to write swap max amount (maxRAT)", "value", maxRAT, "error", err) logg.ErrorCtxf(ctx, "failed to write swap max amount", "value", maxStr, "error", err)
return res, err return res, err
} }
// save swap related data for the swap preview // save swap related data for the swap preview
metadata := &dataserviceapi.TokenHoldings{ metadata := &dataserviceapi.TokenHoldings{
TokenAddress: string(recipientActiveAddress),
TokenSymbol: string(recipientActiveSym), TokenSymbol: string(recipientActiveSym),
Balance: formattedBalance, //not used
TokenDecimals: string(recipientActiveDecimal), TokenDecimals: string(recipientActiveDecimal),
TokenAddress: string(recipientActiveAddress),
} }
// Store the active swap_to data // Store the active swap_to data
@ -409,17 +402,7 @@ func (h *MenuHandlers) MaxAmount(ctx context.Context, sym string, input []byte)
return res, err return res, err
} }
res.Content = l.Get( res.Content = fmt.Sprintf("%s %s", maxStr, string(activeSym))
"Credit Available: %s %s\n(You can swap up to %s %s -> %s %s).\nEnter %s amount:",
maxRAT,
string(recipientActiveSym),
maxSAT,
string(activeSym),
maxRAT,
string(recipientActiveSym),
string(recipientActiveSym),
)
return res, nil return res, nil
} }
@ -489,8 +472,8 @@ func (h *MenuHandlers) resolveActivePoolAddress(ctx context.Context, sessionId s
return nil, err return nil, err
} }
func (h *MenuHandlers) calculateSendCreditLimits(ctx context.Context, poolAddress, fromAddress, toAddress, publicKey, fromDecimal, toDecimal []byte) (string, string, error) { func (h *MenuHandlers) calculateSwapMaxAmount(ctx context.Context, poolAddress, fromAddress, toAddress, publicKey, decimal []byte) (string, error) {
creditSendMaxLimits, err := h.accountService.GetCreditSendMaxLimit( swapLimit, err := h.accountService.GetSwapFromTokenMaxLimit(
ctx, ctx,
string(poolAddress), string(poolAddress),
string(fromAddress), string(fromAddress),
@ -498,17 +481,14 @@ func (h *MenuHandlers) calculateSendCreditLimits(ctx context.Context, poolAddres
string(publicKey), string(publicKey),
) )
if err != nil { if err != nil {
logg.ErrorCtxf(ctx, "failed on GetCreditSendMaxLimit", "error", err) logg.ErrorCtxf(ctx, "failed on GetSwapFromTokenMaxLimit", "error", err)
return "", "", err return "", err
} }
scaledSAT := store.ScaleDownBalance(creditSendMaxLimits.MaxSAT, string(fromDecimal)) scaled := store.ScaleDownBalance(swapLimit.Max, string(decimal))
formattedSAT, _ := store.TruncateDecimalString(string(scaledSAT), 2)
scaledRAT := store.ScaleDownBalance(creditSendMaxLimits.MaxRAT, string(toDecimal)) formattedAmount, _ := store.TruncateDecimalString(string(scaled), 2)
formattedRAT, _ := store.TruncateDecimalString(string(scaledRAT), 2) return formattedAmount, nil
return formattedSAT, formattedRAT, nil
} }
// ValidateAmount ensures that the given input is a valid amount and that // ValidateAmount ensures that the given input is a valid amount and that
@ -604,7 +584,7 @@ func (h *MenuHandlers) GetSender(ctx context.Context, sym string, input []byte)
return res, nil return res, nil
} }
// GetAmount retrieves the transaction amount from the store. // GetAmount retrieves the amount from teh Gdbm Db.
func (h *MenuHandlers) GetAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *MenuHandlers) GetAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
@ -697,7 +677,6 @@ func (h *MenuHandlers) TransactionSwapPreview(ctx context.Context, sym string, i
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
// Input in RAT
inputStr := string(input) inputStr := string(input)
if inputStr == "0" { if inputStr == "0" {
return res, nil return res, nil
@ -722,15 +701,14 @@ func (h *MenuHandlers) TransactionSwapPreview(ctx context.Context, sym string, i
return res, err return res, err
} }
// use the stored max RAT maxValue, err := strconv.ParseFloat(swapData.ActiveSwapMaxAmount, 64)
maxRATValue, err := strconv.ParseFloat(swapData.ActiveSwapMaxAmount, 64)
if err != nil { if err != nil {
logg.ErrorCtxf(ctx, "Failed to convert the swapMaxAmount to a float", "error", err) logg.ErrorCtxf(ctx, "Failed to convert the swapMaxAmount to a float", "error", err)
return res, err return res, err
} }
inputAmount, err := strconv.ParseFloat(inputStr, 64) inputAmount, err := strconv.ParseFloat(inputStr, 64)
if err != nil || inputAmount > maxRATValue { if err != nil || inputAmount > maxValue {
res.FlagSet = append(res.FlagSet, flag_invalid_amount) res.FlagSet = append(res.FlagSet, flag_invalid_amount)
res.Content = inputStr res.Content = inputStr
return res, nil return res, nil
@ -744,33 +722,36 @@ func (h *MenuHandlers) TransactionSwapPreview(ctx context.Context, sym string, i
return res, nil return res, nil
} }
finalAmountStr, err := store.ParseAndScaleAmount(formattedAmount, swapData.ActiveSwapToDecimal) finalAmountStr, err := store.ParseAndScaleAmount(formattedAmount, swapData.ActiveSwapFromDecimal)
if err != nil { if err != nil {
return res, err return res, err
} }
// call the credit send API to get the reverse quote err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SWAP_AMOUNT, []byte(finalAmountStr))
r, err := h.accountService.GetCreditSendReverseQuote(ctx, swapData.ActivePoolAddress, swapData.ActiveSwapFromAddress, swapData.ActiveSwapToAddress, 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
}
// call the API to get the quote
r, err := h.accountService.GetPoolSwapQuote(ctx, finalAmountStr, swapData.PublicKey, swapData.ActiveSwapFromAddress, swapData.ActivePoolAddress, swapData.ActiveSwapToAddress)
if err != nil { if err != nil {
flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
res.FlagSet = append(res.FlagSet, flag_api_error) res.FlagSet = append(res.FlagSet, flag_api_error)
res.Content = l.Get("Your request failed. Please try again later.") res.Content = l.Get("Your request failed. Please try again later.")
logg.ErrorCtxf(ctx, "failed GetCreditSendReverseQuote poolSwap", "error", err) logg.ErrorCtxf(ctx, "failed on poolSwap", "error", err)
return res, nil return res, nil
} }
sendInputAmount := r.InputAmount // amount of SAT that should be swapped // store the outvalue as the final amount
sendOutputAmount := r.OutputAmount // amount of RAT that will be received err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_AMOUNT, []byte(r.OutValue))
// store the sendOutputAmount as the final amount (that will be sent)
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_AMOUNT, []byte(sendOutputAmount))
if err != nil { if err != nil {
logg.ErrorCtxf(ctx, "failed to write output amount value entry with", "key", storedb.DATA_AMOUNT, "value", sendOutputAmount, "error", err) logg.ErrorCtxf(ctx, "failed to write amount value entry with", "key", storedb.DATA_AMOUNT, "value", r.OutValue, "error", err)
return res, err return res, err
} }
// Scale down the quoted output amount // Scale down the quoted amount
quoteAmountStr := store.ScaleDownBalance(sendOutputAmount, swapData.ActiveSwapToDecimal) quoteAmountStr := store.ScaleDownBalance(r.OutValue, swapData.ActiveSwapToDecimal)
// Format the qouteAmount amount to 2 decimal places // Format the qouteAmount amount to 2 decimal places
qouteAmount, _ := store.TruncateDecimalString(quoteAmountStr, 2) qouteAmount, _ := store.TruncateDecimalString(quoteAmountStr, 2)
@ -782,14 +763,7 @@ func (h *MenuHandlers) TransactionSwapPreview(ctx context.Context, sym string, i
return res, err return res, err
} }
// store the sendInputAmount as the swap amount res.Content = fmt.Sprintf(
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SWAP_AMOUNT, []byte(sendInputAmount))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write swap amount entry with", "key", storedb.DATA_ACTIVE_SWAP_AMOUNT, "value", sendInputAmount, "error", err)
return res, err
}
res.Content = l.Get(
"%s will receive %s %s", "%s will receive %s %s",
string(recipientPhoneNumber), qouteAmount, swapData.ActiveSwapToSym, string(recipientPhoneNumber), qouteAmount, swapData.ActiveSwapToSym,
) )

View File

@ -86,7 +86,6 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountService)
ls.DbRs.AddLocalFunc("transaction_reset", appHandlers.TransactionReset) ls.DbRs.AddLocalFunc("transaction_reset", appHandlers.TransactionReset)
ls.DbRs.AddLocalFunc("invite_valid_recipient", appHandlers.InviteValidRecipient) ls.DbRs.AddLocalFunc("invite_valid_recipient", appHandlers.InviteValidRecipient)
ls.DbRs.AddLocalFunc("max_amount", appHandlers.MaxAmount) ls.DbRs.AddLocalFunc("max_amount", appHandlers.MaxAmount)
ls.DbRs.AddLocalFunc("credit_max_amount", appHandlers.MaxAmount)
ls.DbRs.AddLocalFunc("validate_amount", appHandlers.ValidateAmount) ls.DbRs.AddLocalFunc("validate_amount", appHandlers.ValidateAmount)
ls.DbRs.AddLocalFunc("reset_transaction_amount", appHandlers.ResetTransactionAmount) ls.DbRs.AddLocalFunc("reset_transaction_amount", appHandlers.ResetTransactionAmount)
ls.DbRs.AddLocalFunc("get_recipient", appHandlers.GetRecipient) ls.DbRs.AddLocalFunc("get_recipient", appHandlers.GetRecipient)

View File

@ -1 +1,2 @@
{{.max_amount}} Maximum amount: {{.max_amount}}
Enter amount:

View File

@ -1,9 +1,12 @@
LOAD reset_transaction_amount 10 LOAD reset_transaction_amount 0
LOAD max_amount 160 LOAD max_amount 40
RELOAD max_amount RELOAD max_amount
MAP max_amount MAP max_amount
MOUT back 0 MOUT back 0
HALT HALT
LOAD clear_trans_type_flag 6
RELOAD clear_trans_type_flag
CATCH transaction_swap flag_swap_transaction 1
LOAD validate_amount 64 LOAD validate_amount 64
RELOAD validate_amount RELOAD validate_amount
CATCH api_failure flag_api_call_error 1 CATCH api_failure flag_api_call_error 1

View File

@ -1 +1,2 @@
{{.max_amount}} Kiwango cha juu: {{.max_amount}}
Weka kiwango:

View File

@ -1 +0,0 @@
{{.credit_max_amount}}

View File

@ -1,18 +0,0 @@
LOAD reset_transaction_amount 10
LOAD credit_max_amount 160
RELOAD credit_max_amount
MAP credit_max_amount
MOUT back 0
HALT
LOAD clear_trans_type_flag 6
RELOAD clear_trans_type_flag
CATCH transaction_swap flag_swap_transaction 1
LOAD validate_amount 64
RELOAD validate_amount
CATCH api_failure flag_api_call_error 1
CATCH invalid_amount flag_invalid_amount 1
INCMP _ 0
LOAD get_recipient 0
LOAD get_sender 64
LOAD get_amount 32
INCMP transaction_pin *

View File

@ -1 +0,0 @@
{{.credit_max_amount}}

View File

@ -1 +0,0 @@
Enter recipient's phone number/address/alias:

View File

@ -1,12 +0,0 @@
LOAD transaction_reset 0
RELOAD transaction_reset
CATCH no_voucher flag_no_active_voucher 1
MOUT back 0
HALT
LOAD validate_recipient 50
RELOAD validate_recipient
CATCH api_failure flag_api_call_error 1
CATCH invalid_recipient flag_invalid_recipient 1
CATCH invite_recipient flag_invalid_recipient_with_invite 1
INCMP _ 0
INCMP credit_amount *

View File

@ -1 +0,0 @@
Credit-Send

View File

@ -1 +0,0 @@
Tuma-Mkopo

View File

@ -1 +0,0 @@
Weka nambari ya simu/anwani/lakabu:

View File

@ -1 +0,0 @@
Amount {{.transaction_swap_preview}} is invalid, please try again:

View File

@ -1,7 +0,0 @@
MAP transaction_swap_preview
RELOAD reset_transaction_amount
MOUT retry 1
MOUT quit 9
HALT
INCMP ^ 1
INCMP quit 9

View File

@ -1 +0,0 @@
Kiwango {{.transaction_swap_preview}} sio sahihi, tafadhali weka tena:

View File

@ -45,12 +45,3 @@ msgstr "Jina: %s\nSarafu: %s"
msgid "Only USD vouchers are allowed to mpesa.sarafu.eth." msgid "Only USD vouchers are allowed to mpesa.sarafu.eth."
msgstr "Ni sarafu za USD pekee zinazoruhusiwa kwa mpesa.sarafu.eth." msgstr "Ni sarafu za USD pekee zinazoruhusiwa kwa mpesa.sarafu.eth."
msgid "Maximum amount: %s %s\nEnter amount:"
msgstr "Kiwango cha juu: %s %s\nWeka kiwango:"
msgid "Credit Available: %s %s\n(You can swap up to %s %s -> %s %s).\nEnter %s amount:"
msgstr "Kiwango kinachopatikana: %s %s\n(Unaweza kubadilisha hadi %s %s -> %s %s)\nWeka kiwango cha %s:"
msgid "%s will receive %s %s"
msgstr "%s atapokea %s %s"

View File

@ -7,20 +7,18 @@ LOAD check_balance 128
RELOAD check_balance RELOAD check_balance
MAP check_balance MAP check_balance
MOUT send 1 MOUT send 1
MOUT credit_send 2 MOUT swap 2
MOUT swap 3 MOUT vouchers 3
MOUT vouchers 4 MOUT select_pool 4
MOUT select_pool 5 MOUT account 5
MOUT account 6 MOUT help 6
MOUT help 7
MOUT quit 9 MOUT quit 9
HALT HALT
INCMP send 1 INCMP send 1
INCMP credit_send 2 INCMP swap_to_list 2
INCMP swap_to_list 3 INCMP my_vouchers 3
INCMP my_vouchers 4 INCMP select_pool 4
INCMP select_pool 5 INCMP my_account 5
INCMP my_account 6 INCMP help 6
INCMP help 7
INCMP quit 9 INCMP quit 9
INCMP . * INCMP . *

View File

@ -1,6 +1,5 @@
CATCH no_voucher flag_no_active_voucher 1 CATCH no_voucher flag_no_active_voucher 1
LOAD get_pools 0 LOAD get_pools 0
RELOAD get_pools
MAP get_pools MAP get_pools
LOAD get_default_pool 20 LOAD get_default_pool 20
RELOAD get_default_pool RELOAD get_default_pool

View File

@ -1 +1 @@
Weka nambari ya simu/Anwani/Lakabu: Weka nambari ya simu:

View File

@ -1,7 +1,6 @@
LOAD transaction_swap_preview 0 LOAD transaction_swap_preview 0
MAP transaction_swap_preview MAP transaction_swap_preview
CATCH api_failure flag_api_call_error 1 CATCH api_failure flag_api_call_error 1
CATCH invalid_credit_send_amount flag_invalid_amount 1
MOUT back 0 MOUT back 0
MOUT quit 9 MOUT quit 9
LOAD authorize_account 6 LOAD authorize_account 6

View File

@ -1,3 +0,0 @@
{{.transaction_swap_preview}}
Tafadhali weka PIN yako kudhibitisha:

View File

@ -173,9 +173,9 @@ func UpdateSwapToVoucherData(ctx context.Context, store DataStore, sessionId str
logg.InfoCtxf(ctx, "UpdateSwapToVoucherData", "data", data) logg.InfoCtxf(ctx, "UpdateSwapToVoucherData", "data", data)
// Active swap to voucher data entries // Active swap to voucher data entries
activeEntries := map[storedb.DataTyp][]byte{ activeEntries := map[storedb.DataTyp][]byte{
storedb.DATA_ACTIVE_SWAP_TO_ADDRESS: []byte(data.TokenAddress),
storedb.DATA_ACTIVE_SWAP_TO_SYM: []byte(data.TokenSymbol), storedb.DATA_ACTIVE_SWAP_TO_SYM: []byte(data.TokenSymbol),
storedb.DATA_ACTIVE_SWAP_TO_DECIMAL: []byte(data.TokenDecimals), storedb.DATA_ACTIVE_SWAP_TO_DECIMAL: []byte(data.TokenDecimals),
storedb.DATA_ACTIVE_SWAP_TO_ADDRESS: []byte(data.TokenAddress),
} }
// Write active data // Write active data

View File

@ -7,7 +7,6 @@ import (
"math/big" "math/big"
"reflect" "reflect"
"strconv" "strconv"
"strings"
storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db" storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db"
) )
@ -22,34 +21,25 @@ type TransactionData struct {
ActiveAddress string ActiveAddress string
} }
// TruncateDecimalString safely truncates (not rounds) a number string to the specified decimal places // TruncateDecimalString safely truncates the input amount to the specified decimal places
func TruncateDecimalString(input string, decimalPlaces int) (string, error) { func TruncateDecimalString(input string, decimalPlaces int) (string, error) {
if _, err := strconv.ParseFloat(input, 64); err != nil { num, ok := new(big.Float).SetString(input)
if !ok {
return "", fmt.Errorf("invalid input") return "", fmt.Errorf("invalid input")
} }
// Split input into integer and fractional parts // Multiply by 10^decimalPlaces
parts := strings.SplitN(input, ".", 2) scale := new(big.Float).SetInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(decimalPlaces)), nil))
intPart := parts[0] scaled := new(big.Float).Mul(num, scale)
var fracPart string
if len(parts) == 2 { // Truncate by converting to int (chops off decimals)
fracPart = parts[1] intPart, _ := scaled.Int(nil)
}
// Truncate or pad fractional part // Divide back to get truncated float
if len(fracPart) > decimalPlaces { truncated := new(big.Float).Quo(new(big.Float).SetInt(intPart), scale)
fracPart = fracPart[:decimalPlaces]
} else {
fracPart = fracPart + strings.Repeat("0", decimalPlaces-len(fracPart))
}
// Handle zero decimal places // Format with fixed decimals
if decimalPlaces == 0 { return truncated.Text('f', decimalPlaces), nil
return intPart, nil
}
return fmt.Sprintf("%s.%s", intPart, fracPart), nil
} }
func ParseAndScaleAmount(storedAmount, activeDecimal string) (string, error) { func ParseAndScaleAmount(storedAmount, activeDecimal string) (string, error) {

View File

@ -22,13 +22,6 @@ func TestTruncateDecimalString(t *testing.T) {
want: "4.00", want: "4.00",
expectError: false, expectError: false,
}, },
{
name: "precision test",
input: "2.1",
decimalPlaces: 2,
want: "2.10",
expectError: false,
},
{ {
name: "single decimal", name: "single decimal",
input: "4.1", input: "4.1",