Compare commits

..

No commits in common. "master" and "ui-cleanup-update" have entirely different histories.

47 changed files with 263 additions and 903 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.20250630214912-814bef2b209a
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

8
go.sum
View File

@ -20,14 +20,6 @@ git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250630213606-
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250630213606-12940bb5f284/go.mod h1:y/vsN8UO0wSxZk3gv0y5df4RPKMJI6TIxjVcVCPF8T8= git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250630213606-12940bb5f284/go.mod h1:y/vsN8UO0wSxZk3gv0y5df4RPKMJI6TIxjVcVCPF8T8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250630214912-814bef2b209a h1:KuhJ/WY4RCGmrXUA680ciaponM4vM5zBOJfnCpUo2fc= git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250630214912-814bef2b209a h1:KuhJ/WY4RCGmrXUA680ciaponM4vM5zBOJfnCpUo2fc=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250630214912-814bef2b209a/go.mod h1:y/vsN8UO0wSxZk3gv0y5df4RPKMJI6TIxjVcVCPF8T8= git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250630214912-814bef2b209a/go.mod h1:y/vsN8UO0wSxZk3gv0y5df4RPKMJI6TIxjVcVCPF8T8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251021120522-6f7802b58cf5 h1:bQglHVxMilciZ9M2PGuLgA+Wkvqo8OqQh6TFYwjtuSE=
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/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

@ -52,7 +52,7 @@ func TestCheckBalance(t *testing.T) {
alias: "user72", alias: "user72",
activeSym: "SRF", activeSym: "SRF",
activeBal: "10.967", activeBal: "10.967",
expectedResult: resource.Result{Content: "user72\nBalance: 10.96 SRF\n"}, expectedResult: resource.Result{Content: "user72 balance: 10.96 SRF\n"},
expectError: false, expectError: false,
}, },
} }

View File

@ -22,12 +22,12 @@ func (h *MenuHandlers) GetPools(ctx context.Context, sym string, input []byte) (
} }
userStore := h.userdataStore userStore := h.userdataStore
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error") flag_api_error, _ := h.flagManager.GetFlag("flag_api_error")
// call the api to get a list of top 5 pools sorted by swaps // call the api to get a list of top 5 pools sorted by swaps
topPools, err := h.accountService.FetchTopPools(ctx) topPools, err := h.accountService.FetchTopPools(ctx)
if err != nil { if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error) res.FlagSet = append(res.FlagSet, flag_api_error)
logg.ErrorCtxf(ctx, "failed on FetchTransactions", "error", err) logg.ErrorCtxf(ctx, "failed on FetchTransactions", "error", err)
return res, err return res, err
} }
@ -129,12 +129,12 @@ func (h *MenuHandlers) ViewPool(ctx context.Context, sym string, input []byte) (
} }
if poolData == nil { if poolData == nil {
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error") flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
// no match found. Call the API using the inputStr as the symbol // no match found. Call the API using the inputStr as the symbol
poolResp, err := h.accountService.RetrievePoolDetails(ctx, inputStr) poolResp, err := h.accountService.RetrievePoolDetails(ctx, inputStr)
if err != nil { if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error) res.FlagSet = append(res.FlagSet, flag_api_error)
return res, nil return res, nil
} }

View File

@ -41,7 +41,7 @@ func (h *MenuHandlers) LoadSwapToList(ctx context.Context, sym string, input []b
l.AddDomain("default") l.AddDomain("default")
flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher") flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher")
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error") flag_api_error, _ := h.flagManager.GetFlag("flag_api_error")
inputStr := string(input) inputStr := string(input)
if inputStr == "0" { if inputStr == "0" {
@ -88,7 +88,7 @@ func (h *MenuHandlers) LoadSwapToList(ctx context.Context, sym string, input []b
// call the api using the ActivePoolAddress and ActiveVoucherAddress to check if it is part of the pool // call the api using the ActivePoolAddress and ActiveVoucherAddress to check if it is part of the pool
r, err := h.accountService.CheckTokenInPool(ctx, string(activePoolAddress), string(activeAddress)) r, err := h.accountService.CheckTokenInPool(ctx, string(activePoolAddress), string(activeAddress))
if err != nil { if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_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)
return res, err return res, err
} }
@ -110,7 +110,7 @@ func (h *MenuHandlers) LoadSwapToList(ctx context.Context, sym string, input []b
// call the api using the activePoolAddress to get a list of SwapToSymbolsData // call the api using the activePoolAddress to get a list of SwapToSymbolsData
swapToList, err := h.accountService.GetPoolSwappableVouchers(ctx, string(activePoolAddress)) swapToList, err := h.accountService.GetPoolSwappableVouchers(ctx, string(activePoolAddress))
if err != nil { if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error) res.FlagSet = append(res.FlagSet, flag_api_error)
logg.ErrorCtxf(ctx, "failed on FetchTransactions", "error", err) logg.ErrorCtxf(ctx, "failed on FetchTransactions", "error", err)
return res, err return res, err
} }
@ -165,7 +165,7 @@ func (h *MenuHandlers) SwapMaxLimit(ctx context.Context, sym string, input []byt
} }
flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher") flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher")
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error") flag_api_error, _ := h.flagManager.GetFlag("flag_api_error")
flag_low_swap_amount, _ := h.flagManager.GetFlag("flag_low_swap_amount") flag_low_swap_amount, _ := h.flagManager.GetFlag("flag_low_swap_amount")
res.FlagReset = append(res.FlagReset, flag_incorrect_voucher, flag_low_swap_amount) res.FlagReset = append(res.FlagReset, flag_incorrect_voucher, flag_low_swap_amount)
@ -202,9 +202,9 @@ func (h *MenuHandlers) SwapMaxLimit(ctx context.Context, sym string, input []byt
logg.InfoCtxf(ctx, "Call GetSwapFromTokenMaxLimit with:", "ActivePoolAddress", swapData.ActivePoolAddress, "ActiveSwapFromAddress", swapData.ActiveSwapFromAddress, "ActiveSwapToAddress", swapData.ActiveSwapToAddress, "publicKey", swapData.PublicKey) logg.InfoCtxf(ctx, "Call GetSwapFromTokenMaxLimit with:", "ActivePoolAddress", swapData.ActivePoolAddress, "ActiveSwapFromAddress", swapData.ActiveSwapFromAddress, "ActiveSwapToAddress", swapData.ActiveSwapToAddress, "publicKey", swapData.PublicKey)
r, err := h.accountService.GetSwapFromTokenMaxLimit(ctx, swapData.ActivePoolAddress, swapData.ActiveSwapFromAddress, swapData.ActiveSwapToAddress, swapData.PublicKey) r, err := h.accountService.GetSwapFromTokenMaxLimit(ctx, swapData.ActivePoolAddress, swapData.ActiveSwapFromAddress, swapData.ActiveSwapToAddress, swapData.PublicKey)
if err != nil { if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error) res.FlagSet = append(res.FlagSet, flag_api_error)
logg.ErrorCtxf(ctx, "failed on GetSwapFromTokenMaxLimit", "error", err) logg.ErrorCtxf(ctx, "failed on GetSwapFromTokenMaxLimit", "error", err)
return res, nil return res, err
} }
// Scale down the amount // Scale down the amount
@ -220,7 +220,7 @@ func (h *MenuHandlers) SwapMaxLimit(ctx context.Context, sym string, input []byt
} }
// Format to 2 decimal places // Format to 2 decimal places
maxStr, _ := store.TruncateDecimalString(string(maxAmountStr), 2) maxStr := fmt.Sprintf("%.2f", maxAmountFloat)
if maxAmountFloat < 0.1 { if maxAmountFloat < 0.1 {
// return with low amount flag // return with low amount flag
@ -310,8 +310,8 @@ func (h *MenuHandlers) SwapPreview(ctx context.Context, sym string, input []byte
// call the API to get the quote // call the API to get the quote
r, err := h.accountService.GetPoolSwapQuote(ctx, finalAmountStr, swapData.PublicKey, swapData.ActiveSwapFromAddress, swapData.ActivePoolAddress, swapData.ActiveSwapToAddress) r, err := h.accountService.GetPoolSwapQuote(ctx, finalAmountStr, swapData.PublicKey, swapData.ActiveSwapFromAddress, swapData.ActivePoolAddress, swapData.ActiveSwapToAddress)
if err != nil { if err != nil {
flag_api_call_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_call_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 on poolSwap", "error", err) logg.ErrorCtxf(ctx, "failed on poolSwap", "error", err)
return res, nil return res, nil
@ -319,9 +319,14 @@ func (h *MenuHandlers) SwapPreview(ctx context.Context, sym string, input []byte
// Scale down the quoted amount // Scale down the quoted amount
quoteAmountStr := store.ScaleDownBalance(r.OutValue, swapData.ActiveSwapToDecimal) quoteAmountStr := store.ScaleDownBalance(r.OutValue, swapData.ActiveSwapToDecimal)
qouteAmount, err := strconv.ParseFloat(quoteAmountStr, 64)
if err != nil {
logg.ErrorCtxf(ctx, "failed to parse quoteAmountStr as float", "value", quoteAmountStr, "error", err)
return res, err
}
// Format to 2 decimal places // Format to 2 decimal places
qouteStr, _ := store.TruncateDecimalString(string(quoteAmountStr), 2) qouteStr := fmt.Sprintf("%.2f", qouteAmount)
res.Content = fmt.Sprintf( res.Content = fmt.Sprintf(
"You will swap:\n%s %s for %s %s:", "You will swap:\n%s %s for %s %s:",
@ -364,8 +369,8 @@ func (h *MenuHandlers) InitiateSwap(ctx context.Context, sym string, input []byt
// Call the poolSwap API // Call the poolSwap API
r, err := h.accountService.PoolSwap(ctx, swapAmountStr, swapData.PublicKey, swapData.ActiveSwapFromAddress, swapData.ActivePoolAddress, swapData.ActiveSwapToAddress) r, err := h.accountService.PoolSwap(ctx, swapAmountStr, swapData.PublicKey, swapData.ActiveSwapFromAddress, swapData.ActivePoolAddress, swapData.ActiveSwapToAddress)
if err != nil { if err != nil {
flag_api_call_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_call_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 on poolSwap", "error", err) logg.ErrorCtxf(ctx, "failed on poolSwap", "error", err)
return res, nil return res, nil

View File

@ -2,246 +2,140 @@ package application
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
"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/common/hex"
"git.grassecon.net/grassrootseconomics/common/identity" "git.grassecon.net/grassrootseconomics/common/identity"
"git.grassecon.net/grassrootseconomics/common/phone" "git.grassecon.net/grassrootseconomics/common/phone"
"git.grassecon.net/grassrootseconomics/sarafu-api/remote/http" "git.grassecon.net/grassrootseconomics/sarafu-api/models"
"git.grassecon.net/grassrootseconomics/sarafu-vise/config" "git.grassecon.net/grassrootseconomics/sarafu-vise/config"
"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"
"github.com/grassrootseconomics/ethutils" "github.com/grassrootseconomics/ethutils"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
"gopkg.in/leonelquinteros/gotext.v1" "gopkg.in/leonelquinteros/gotext.v1"
) )
// ValidateRecipient validates that the given input is valid. // ValidateRecipient validates that the given input is valid.
//
// TODO: split up functino
func (h *MenuHandlers) ValidateRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *MenuHandlers) ValidateRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
var AliasAddressResult string
var AliasAddress *models.AliasAddress
store := h.userdataStore store := h.userdataStore
flag_invalid_recipient, _ := h.flagManager.GetFlag("flag_invalid_recipient")
sessionId, ok := ctx.Value("SessionId").(string) sessionId, ok := ctx.Value("SessionId").(string)
if !ok { if !ok {
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
flag_invalid_recipient, _ := h.flagManager.GetFlag("flag_invalid_recipient")
flag_invalid_recipient_with_invite, _ := h.flagManager.GetFlag("flag_invalid_recipient_with_invite")
flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
// remove white spaces // remove white spaces
recipient := strings.ReplaceAll(string(input), " ", "") recipient := strings.ReplaceAll(string(input), " ", "")
if recipient == "0" {
return res, nil
}
recipientType, err := identity.CheckRecipient(recipient) if recipient != "0" {
if err != nil { recipientType, err := identity.CheckRecipient(recipient)
// Invalid recipient format (not a phone number, address, or valid alias format) if err != nil {
res.FlagSet = append(res.FlagSet, flag_invalid_recipient) // Invalid recipient format (not a phone number, address, or valid alias format)
res.Content = recipient res.FlagSet = append(res.FlagSet, flag_invalid_recipient)
return res, nil
}
// save the recipient as the temporaryRecipient
err = store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(recipient))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write temporaryRecipient entry with", "key", storedb.DATA_TEMPORARY_VALUE, "value", recipient, "error", err)
return res, err
}
switch recipientType {
case "phone number":
return h.handlePhoneNumber(ctx, sessionId, recipient, &res)
case "address":
return h.handleAddress(ctx, sessionId, recipient, &res)
case "alias":
return h.handleAlias(ctx, sessionId, recipient, &res)
}
return res, nil
}
func (h *MenuHandlers) handlePhoneNumber(ctx context.Context, sessionId, recipient string, res *resource.Result) (resource.Result, error) {
store := h.userdataStore
flag_invalid_recipient_with_invite, _ := h.flagManager.GetFlag("flag_invalid_recipient_with_invite")
formattedNumber, err := phone.FormatPhoneNumber(recipient)
if err != nil {
logg.ErrorCtxf(ctx, "Failed to format phone number", "recipient", recipient, "error", err)
return *res, err
}
publicKey, err := store.ReadEntry(ctx, formattedNumber, storedb.DATA_PUBLIC_KEY)
if err != nil {
if db.IsNotFound(err) {
logg.InfoCtxf(ctx, "Unregistered phone number", "recipient", recipient)
res.FlagSet = append(res.FlagSet, flag_invalid_recipient_with_invite)
res.Content = recipient res.Content = recipient
return *res, nil
return res, nil
} }
logg.ErrorCtxf(ctx, "Failed to read publicKey", "error", err)
return *res, err
}
if err := store.WriteEntry(ctx, sessionId, storedb.DATA_RECIPIENT, publicKey); err != nil { // save the recipient as the temporaryRecipient
logg.ErrorCtxf(ctx, "Failed to write recipient", "value", string(publicKey), "error", err) err = store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(recipient))
return *res, err if err != nil {
} logg.ErrorCtxf(ctx, "failed to write temporaryRecipient entry with", "key", storedb.DATA_TEMPORARY_VALUE, "value", recipient, "error", err)
return res, err
// Delegate to shared logic
if err := h.determineAndSaveTransactionType(ctx, sessionId, publicKey, []byte(formattedNumber)); err != nil {
return *res, err
}
return *res, nil
}
func (h *MenuHandlers) handleAddress(ctx context.Context, sessionId, recipient string, res *resource.Result) (resource.Result, error) {
store := h.userdataStore
address := ethutils.ChecksumAddress(recipient)
if err := store.WriteEntry(ctx, sessionId, storedb.DATA_RECIPIENT, []byte(address)); err != nil {
logg.ErrorCtxf(ctx, "Failed to write recipient address", "error", err)
return *res, err
}
// normalize the address to fetch the recipient's phone number
publicKeyNormalized, err := hex.NormalizeHex(address)
if err != nil {
return *res, err
}
// get the recipient's phone number from the address
recipientPhoneNumber, err := store.ReadEntry(ctx, publicKeyNormalized, storedb.DATA_PUBLIC_KEY_REVERSE)
if err != nil || len(recipientPhoneNumber) == 0 {
logg.WarnCtxf(ctx, "Recipient address not registered, switching to normal transaction", "address", address)
recipientPhoneNumber = nil
}
if err := h.determineAndSaveTransactionType(ctx, sessionId, []byte(address), recipientPhoneNumber); err != nil {
return *res, err
}
return *res, nil
}
func (h *MenuHandlers) handleAlias(ctx context.Context, sessionId, recipient string, res *resource.Result) (resource.Result, error) {
store := h.userdataStore
flag_invalid_recipient, _ := h.flagManager.GetFlag("flag_invalid_recipient")
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
var aliasAddressResult string
if strings.Contains(recipient, ".") {
alias, err := h.accountService.CheckAliasAddress(ctx, recipient)
if err == nil {
aliasAddressResult = alias.Address
} else {
logg.ErrorCtxf(ctx, "Failed to resolve alias", "alias", recipient, "error", err)
} }
} else {
for _, domain := range config.SearchDomains() {
fqdn := fmt.Sprintf("%s.%s", recipient, domain)
logg.InfoCtxf(ctx, "Trying alias", "fqdn", fqdn)
alias, err := h.accountService.CheckAliasAddress(ctx, fqdn) switch recipientType {
if err == nil { case "phone number":
res.FlagReset = append(res.FlagReset, flag_api_call_error) // format the phone number
aliasAddressResult = alias.Address formattedNumber, err := phone.FormatPhoneNumber(recipient)
break if err != nil {
logg.ErrorCtxf(ctx, "Failed to format the phone number: %s", recipient, "error", err)
return res, err
}
// Check if the phone number is registered
publicKey, err := store.ReadEntry(ctx, formattedNumber, storedb.DATA_PUBLIC_KEY)
if err != nil {
if db.IsNotFound(err) {
logg.InfoCtxf(ctx, "Unregistered phone number: %s", recipient)
res.FlagSet = append(res.FlagSet, flag_invalid_recipient_with_invite)
res.Content = recipient
return res, nil
}
logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", storedb.DATA_PUBLIC_KEY, "error", err)
return res, err
}
// Save the publicKey as the recipient
err = store.WriteEntry(ctx, sessionId, storedb.DATA_RECIPIENT, publicKey)
if err != nil {
logg.ErrorCtxf(ctx, "failed to write recipient entry with", "key", storedb.DATA_RECIPIENT, "value", string(publicKey), "error", err)
return res, err
}
case "address":
// checksum the address
address := ethutils.ChecksumAddress(recipient)
// Save the valid Ethereum address as the recipient
err = store.WriteEntry(ctx, sessionId, storedb.DATA_RECIPIENT, []byte(address))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write recipient entry with", "key", storedb.DATA_RECIPIENT, "value", recipient, "error", err)
return res, err
}
case "alias":
if strings.Contains(recipient, ".") {
AliasAddress, err = h.accountService.CheckAliasAddress(ctx, recipient)
if err == nil {
AliasAddressResult = AliasAddress.Address
} else {
logg.ErrorCtxf(ctx, "failed to resolve alias", "alias", recipient, "error_alias_check", err)
}
} else { } else {
res.FlagSet = append(res.FlagSet, flag_api_call_error) //Perform a search for each search domain,break on first match
logg.ErrorCtxf(ctx, "Alias resolution failed", "alias", fqdn, "error", err) for _, domain := range config.SearchDomains() {
return *res, nil fqdn := fmt.Sprintf("%s.%s", recipient, domain)
logg.InfoCtxf(ctx, "Resolving with fqdn alias", "alias", fqdn)
AliasAddress, err = h.accountService.CheckAliasAddress(ctx, fqdn)
if err == nil {
res.FlagReset = append(res.FlagReset, flag_api_error)
AliasAddressResult = AliasAddress.Address
continue
} else {
res.FlagSet = append(res.FlagSet, flag_api_error)
logg.ErrorCtxf(ctx, "failed to resolve alias", "alias", recipient, "error_alias_check", err)
return res, nil
}
}
}
if AliasAddressResult == "" {
res.Content = recipient
res.FlagSet = append(res.FlagSet, flag_invalid_recipient)
return res, nil
} else {
err = store.WriteEntry(ctx, sessionId, storedb.DATA_RECIPIENT, []byte(AliasAddressResult))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write recipient entry with", "key", storedb.DATA_RECIPIENT, "value", AliasAddressResult, "error", err)
return res, err
}
} }
} }
} }
if aliasAddressResult == "" { return res, nil
res.FlagSet = append(res.FlagSet, flag_invalid_recipient)
res.Content = recipient
return *res, nil
}
if err := store.WriteEntry(ctx, sessionId, storedb.DATA_RECIPIENT, []byte(aliasAddressResult)); err != nil {
logg.ErrorCtxf(ctx, "Failed to store alias recipient", "error", err)
return *res, err
}
// Normalize the alias address to fetch the recipient's phone number
publicKeyNormalized, err := hex.NormalizeHex(aliasAddressResult)
if err != nil {
logg.ErrorCtxf(ctx, "Failed to normalize alias address", "address", aliasAddressResult, "error", err)
return *res, err
}
// get the recipient's phone number from the address
recipientPhoneNumber, err := store.ReadEntry(ctx, publicKeyNormalized, storedb.DATA_PUBLIC_KEY_REVERSE)
if err != nil || len(recipientPhoneNumber) == 0 {
logg.WarnCtxf(ctx, "Alias address not registered, switching to normal transaction", "address", aliasAddressResult)
recipientPhoneNumber = nil
}
if err := h.determineAndSaveTransactionType(ctx, sessionId, []byte(aliasAddressResult), recipientPhoneNumber); err != nil {
return *res, err
}
return *res, nil
}
// determineAndSaveTransactionType centralizes transaction-type logic and recipient info persistence.
// It expects the session to already have the recipient's public key (address) written.
func (h *MenuHandlers) determineAndSaveTransactionType(
ctx context.Context,
sessionId string,
publicKey []byte,
recipientPhoneNumber []byte,
) error {
store := h.userdataStore
txType := "swap"
// Read sender's active address
senderActiveAddress, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_ADDRESS)
if err != nil {
logg.ErrorCtxf(ctx, "Failed to read sender active address", "error", err)
return err
}
var recipientActiveAddress []byte
if recipientPhoneNumber != nil {
recipientActiveAddress, _ = store.ReadEntry(ctx, string(recipientPhoneNumber), storedb.DATA_ACTIVE_ADDRESS)
}
// recipient has no active token → normal transaction
if recipientActiveAddress == nil {
txType = "normal"
} else if senderActiveAddress != nil && string(senderActiveAddress) == string(recipientActiveAddress) {
// recipient has active token same as sender → normal transaction
txType = "normal"
}
// Save the transaction type
if err := store.WriteEntry(ctx, sessionId, storedb.DATA_SEND_TRANSACTION_TYPE, []byte(txType)); err != nil {
logg.ErrorCtxf(ctx, "Failed to write transaction type", "type", txType, "error", err)
return err
}
// Save the recipient's phone number only if it exists
if recipientPhoneNumber != nil {
if err := store.WriteEntry(ctx, sessionId, storedb.DATA_RECIPIENT_PHONE_NUMBER, recipientPhoneNumber); err != nil {
logg.ErrorCtxf(ctx, "Failed to write recipient phone number", "type", txType, "error", err)
return err
}
} else {
logg.InfoCtxf(ctx, "No recipient phone number found for public key", "publicKey", string(publicKey))
}
return nil
} }
// TransactionReset resets the previous transaction data (Recipient and Amount) // TransactionReset resets the previous transaction data (Recipient and Amount)
@ -268,16 +162,6 @@ func (h *MenuHandlers) TransactionReset(ctx context.Context, sym string, input [
return res, nil return res, nil
} }
err = store.WriteEntry(ctx, sessionId, storedb.DATA_SEND_TRANSACTION_TYPE, []byte(""))
if err != nil {
return res, nil
}
err = store.WriteEntry(ctx, sessionId, storedb.DATA_RECIPIENT_PHONE_NUMBER, []byte(""))
if err != nil {
return res, nil
}
res.FlagReset = append(res.FlagReset, flag_invalid_recipient, flag_invalid_recipient_with_invite) res.FlagReset = append(res.FlagReset, flag_invalid_recipient, flag_invalid_recipient_with_invite)
return res, nil return res, nil
@ -294,226 +178,40 @@ 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
} }
// MaxAmount checks the transaction type to determine the displayed max amount. // MaxAmount gets the current balance from the API and sets it as
// If the transaction type is "swap", it checks the max swappable amount and sets this as the content.
// If the transaction type is "normal", gets the current sender's balance from the store and sets it as
// the result content. // the result content.
func (h *MenuHandlers) MaxAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *MenuHandlers) MaxAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
var err error
sessionId, ok := ctx.Value("SessionId").(string) sessionId, ok := ctx.Value("SessionId").(string)
if !ok { if !ok {
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
store := h.userdataStore
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error") activeBal, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_BAL)
flag_swap_transaction, _ := h.flagManager.GetFlag("flag_swap_transaction")
userStore := h.userdataStore
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
// Fetch session data
transactionType, activeBal, activeSym, activeAddress, publicKey, activeDecimal, err := h.getSessionData(ctx, sessionId)
if err != nil { if err != nil {
logg.ErrorCtxf(ctx, "failed to read activeBal entry with", "key", storedb.DATA_ACTIVE_BAL, "error", err)
return res, err return res, err
} }
// Format the active balance amount to 2 decimal places res.Content = string(activeBal)
formattedBalance, _ := store.TruncateDecimalString(string(activeBal), 2)
// If normal transaction, or if the sym is max_amount, return balance
if string(transactionType) == "normal" || sym == "max_amount" {
res.FlagReset = append(res.FlagReset, flag_swap_transaction)
res.Content = l.Get("Maximum amount: %s %s\nEnter amount:", formattedBalance, string(activeSym))
return res, nil
}
res.FlagSet = append(res.FlagSet, flag_swap_transaction)
// Get the recipient's phone number to read other data items
recipientPhoneNumber, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_RECIPIENT_PHONE_NUMBER)
if err != nil {
// invalid state
return res, err
}
recipientActiveSym, recipientActiveAddress, recipientActiveDecimal, err := h.getRecipientData(ctx, string(recipientPhoneNumber))
if err != nil {
return res, err
}
// Resolve active pool address
activePoolAddress, err := h.resolveActivePoolAddress(ctx, sessionId)
if err != nil {
return res, err
}
// Check if sender token is swappable
canSwap, err := h.accountService.CheckTokenInPool(ctx, string(activePoolAddress), string(activeAddress))
if err != nil || !canSwap.CanSwapFrom {
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
logg.ErrorCtxf(ctx, "failed on CheckTokenInPool", "error", err)
return res, nil
}
res.FlagReset = append(res.FlagReset, flag_swap_transaction)
res.Content = l.Get("Maximum amount: %s %s\nEnter amount:", formattedBalance, string(activeSym))
return res, nil
}
// retrieve the max credit send amounts
maxSAT, maxRAT, err := h.calculateSendCreditLimits(ctx, activePoolAddress, activeAddress, recipientActiveAddress, publicKey, activeDecimal, recipientActiveDecimal)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
logg.ErrorCtxf(ctx, "failed on calculateSendCreditLimits", "error", err)
return res, nil
}
// Fallback if below minimum
maxFloat, _ := strconv.ParseFloat(maxSAT, 64)
if maxFloat < 0.1 {
res.FlagReset = append(res.FlagReset, flag_swap_transaction)
res.Content = l.Get("Maximum amount: %s %s\nEnter amount:", formattedBalance, string(activeSym))
return res, nil
}
// Save max RAT amount to be used in validating the user's input
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SWAP_MAX_AMOUNT, []byte(maxRAT))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write swap max amount (maxRAT)", "value", maxRAT, "error", err)
return res, err
}
// save swap related data for the swap preview
metadata := &dataserviceapi.TokenHoldings{
TokenAddress: string(recipientActiveAddress),
TokenSymbol: string(recipientActiveSym),
TokenDecimals: string(recipientActiveDecimal),
}
// 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
}
res.Content = l.Get(
"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
} }
func (h *MenuHandlers) getSessionData(ctx context.Context, sessionId string) (transactionType, activeBal, activeSym, activeAddress, publicKey, activeDecimal []byte, err error) {
store := h.userdataStore
transactionType, err = store.ReadEntry(ctx, sessionId, storedb.DATA_SEND_TRANSACTION_TYPE)
if err != nil {
return
}
activeBal, err = store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_BAL)
if err != nil {
return
}
activeAddress, err = store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_ADDRESS)
if err != nil {
return
}
activeSym, err = store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM)
if err != nil {
return
}
publicKey, err = store.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY)
if err != nil {
return
}
activeDecimal, err = store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_DECIMAL)
if err != nil {
return
}
return
}
func (h *MenuHandlers) getRecipientData(ctx context.Context, sessionId string) (recipientActiveSym, recipientActiveAddress, recipientActiveDecimal []byte, err error) {
store := h.userdataStore
recipientActiveSym, err = store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM)
if err != nil {
return
}
recipientActiveAddress, err = store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_ADDRESS)
if err != nil {
return
}
recipientActiveDecimal, err = store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_DECIMAL)
if err != nil {
return
}
return
}
func (h *MenuHandlers) resolveActivePoolAddress(ctx context.Context, sessionId string) ([]byte, error) {
store := h.userdataStore
addr, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_POOL_ADDRESS)
if err == nil {
return addr, nil
}
if db.IsNotFound(err) {
defaultAddr := []byte(config.DefaultPoolAddress())
if err := store.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_POOL_ADDRESS, defaultAddr); err != nil {
logg.ErrorCtxf(ctx, "failed to write default pool address", "error", err)
return nil, err
}
return defaultAddr, nil
}
logg.ErrorCtxf(ctx, "failed to read active pool address", "error", err)
return nil, err
}
func (h *MenuHandlers) calculateSendCreditLimits(ctx context.Context, poolAddress, fromAddress, toAddress, publicKey, fromDecimal, toDecimal []byte) (string, string, error) {
creditSendMaxLimits, err := h.accountService.GetCreditSendMaxLimit(
ctx,
string(poolAddress),
string(fromAddress),
string(toAddress),
string(publicKey),
)
if err != nil {
logg.ErrorCtxf(ctx, "failed on GetCreditSendMaxLimit", "error", err)
return "", "", err
}
scaledSAT := store.ScaleDownBalance(creditSendMaxLimits.MaxSAT, string(fromDecimal))
formattedSAT, _ := store.TruncateDecimalString(string(scaledSAT), 2)
scaledRAT := store.ScaleDownBalance(creditSendMaxLimits.MaxRAT, string(toDecimal))
formattedRAT, _ := store.TruncateDecimalString(string(scaledRAT), 2)
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
// it is not more than the current balance. // it is not more than the current balance.
func (h *MenuHandlers) ValidateAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *MenuHandlers) ValidateAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) {
@ -585,7 +283,7 @@ func (h *MenuHandlers) GetRecipient(ctx context.Context, sym string, input []byt
recipient, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE) recipient, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE)
if len(recipient) == 0 { if len(recipient) == 0 {
logg.ErrorCtxf(ctx, "recipient is empty", "key", storedb.DATA_TEMPORARY_VALUE) logg.ErrorCtxf(ctx, "recipient is empty", "key", storedb.DATA_TEMPORARY_VALUE)
return res, fmt.Errorf("data error encountered") return res, fmt.Errorf("Data error encountered")
} }
res.Content = string(recipient) res.Content = string(recipient)
@ -607,7 +305,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
@ -659,20 +357,9 @@ func (h *MenuHandlers) InitiateTransaction(ctx context.Context, sym string, inpu
// Call TokenTransfer // Call TokenTransfer
r, err := h.accountService.TokenTransfer(ctx, finalAmountStr, data.PublicKey, data.Recipient, data.ActiveAddress) r, err := h.accountService.TokenTransfer(ctx, finalAmountStr, data.PublicKey, data.Recipient, data.ActiveAddress)
if err != nil { if err != nil {
var apiErr *http.APIError flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
if errors.As(err, &apiErr) { res.FlagSet = append(res.FlagSet, flag_api_error)
switch apiErr.Code { res.Content = l.Get("Your request failed. Please try again later.")
case "E10":
res.Content = l.Get("Only USD vouchers are allowed to mpesa.sarafu.eth.")
default:
res.Content = l.Get("Your request failed. Please try again later.")
}
} else {
res.Content = l.Get("An unexpected error occurred. Please try again later.")
}
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
res.FlagSet = append(res.FlagSet, flag_api_call_error)
logg.ErrorCtxf(ctx, "failed on TokenTransfer", "error", err) logg.ErrorCtxf(ctx, "failed on TokenTransfer", "error", err)
return res, nil return res, nil
} }
@ -691,214 +378,3 @@ func (h *MenuHandlers) InitiateTransaction(ctx context.Context, sym string, inpu
res.FlagReset = append(res.FlagReset, flag_account_authorized) res.FlagReset = append(res.FlagReset, flag_account_authorized)
return res, nil return res, nil
} }
// TransactionSwapPreview displays the send swap preview and estimates
func (h *MenuHandlers) TransactionSwapPreview(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")
}
// Input in RAT
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
recipientPhoneNumber, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_RECIPIENT_PHONE_NUMBER)
if err != nil {
// invalid state
return res, err
}
swapData, err := store.ReadSwapPreviewData(ctx, userStore, sessionId)
if err != nil {
return res, err
}
// use the stored max RAT
maxRATValue, 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 > maxRATValue {
res.FlagSet = append(res.FlagSet, flag_invalid_amount)
res.Content = inputStr
return res, nil
}
// Format the amount to 2 decimal places
formattedAmount, err := store.TruncateDecimalString(inputStr, 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)
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)
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 GetCreditSendReverseQuote poolSwap", "error", err)
return res, nil
}
sendInputAmount := r.InputAmount // amount of SAT that should be swapped
sendOutputAmount := r.OutputAmount // amount of RAT that will be received
// store the sendOutputAmount as the final amount (that will be sent)
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_AMOUNT, []byte(sendOutputAmount))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write output amount value entry with", "key", storedb.DATA_AMOUNT, "value", sendOutputAmount, "error", err)
return res, err
}
// Scale down the quoted output amount
quoteAmountStr := store.ScaleDownBalance(sendOutputAmount, swapData.ActiveSwapToDecimal)
// Format the qouteAmount amount to 2 decimal places
qouteAmount, _ := store.TruncateDecimalString(quoteAmountStr, 2)
// store the qouteAmount in the temporary value
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(qouteAmount))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write temporary qouteAmount entry with", "key", storedb.DATA_TEMPORARY_VALUE, "value", qouteAmount, "error", err)
return res, err
}
// store the sendInputAmount as the swap amount
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",
string(recipientPhoneNumber), qouteAmount, swapData.ActiveSwapToSym,
)
return res, nil
}
// TransactionInitiateSwap calls the poolSwap and returns a confirmation based on the result.
func (h *MenuHandlers) TransactionInitiateSwap(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")
flag_swap_transaction, _ := h.flagManager.GetFlag("flag_swap_transaction")
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
}
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
poolSwap, err := h.accountService.PoolSwap(ctx, swapAmountStr, swapData.PublicKey, swapData.ActiveSwapFromAddress, swapData.ActivePoolAddress, swapData.ActiveSwapToAddress)
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
}
swapTrackingId := poolSwap.TrackingId
logg.InfoCtxf(ctx, "poolSwap", "swapTrackingId", swapTrackingId)
// Initiate a send
recipientPublicKey, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_RECIPIENT)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read swapAmount entry with", "key", storedb.DATA_ACTIVE_SWAP_AMOUNT, "error", err)
return res, err
}
recipientPhoneNumber, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_RECIPIENT_PHONE_NUMBER)
if err != nil {
// invalid state
return res, err
}
// read the amount that should be sent
amount, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_AMOUNT)
if err != nil {
// invalid state
return res, err
}
// Call TokenTransfer with the expected swap amount
tokenTransfer, err := h.accountService.TokenTransfer(ctx, string(amount), swapData.PublicKey, string(recipientPublicKey), swapData.ActiveSwapToAddress)
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 TokenTransfer", "error", err)
return res, nil
}
trackingId := tokenTransfer.TrackingId
logg.InfoCtxf(ctx, "TokenTransfer", "trackingId", trackingId)
res.Content = l.Get(
"Your request has been sent. %s will receive %s %s from %s.",
string(recipientPhoneNumber),
swapData.TemporaryValue,
swapData.ActiveSwapToSym,
sessionId,
)
res.FlagReset = append(res.FlagReset, flag_account_authorized, flag_swap_transaction)
return res, nil
}
// ClearTransactionTypeFlag resets the flag when a user goes back.
func (h *MenuHandlers) ClearTransactionTypeFlag(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
flag_swap_transaction, _ := h.flagManager.GetFlag("flag_swap_transaction")
inputStr := string(input)
if inputStr == "0" {
res.FlagReset = append(res.FlagReset, flag_swap_transaction)
return res, nil
}
return res, nil
}

View File

@ -20,7 +20,7 @@ func (h *MenuHandlers) CheckTransactions(ctx context.Context, sym string, input
} }
flag_no_transfers, _ := h.flagManager.GetFlag("flag_no_transfers") flag_no_transfers, _ := h.flagManager.GetFlag("flag_no_transfers")
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error") flag_api_error, _ := h.flagManager.GetFlag("flag_api_error")
userStore := h.userdataStore userStore := h.userdataStore
logdb := h.logDb logdb := h.logDb
@ -33,11 +33,11 @@ func (h *MenuHandlers) CheckTransactions(ctx context.Context, sym string, input
// Fetch transactions from the API using the public key // Fetch transactions from the API using the public key
transactionsResp, err := h.accountService.FetchTransactions(ctx, string(publicKey)) transactionsResp, err := h.accountService.FetchTransactions(ctx, string(publicKey))
if err != nil { if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error) res.FlagSet = append(res.FlagSet, flag_api_error)
logg.ErrorCtxf(ctx, "failed on FetchTransactions", "error", err) logg.ErrorCtxf(ctx, "failed on FetchTransactions", "error", err)
return res, err return res, err
} }
res.FlagReset = append(res.FlagReset, flag_api_call_error) res.FlagReset = append(res.FlagReset, flag_api_error)
// Return if there are no transactions // Return if there are no transactions
if len(transactionsResp) == 0 { if len(transactionsResp) == 0 {

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)
@ -137,10 +136,6 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountService)
ls.DbRs.AddLocalFunc("swap_max_limit", appHandlers.SwapMaxLimit) ls.DbRs.AddLocalFunc("swap_max_limit", appHandlers.SwapMaxLimit)
ls.DbRs.AddLocalFunc("swap_preview", appHandlers.SwapPreview) ls.DbRs.AddLocalFunc("swap_preview", appHandlers.SwapPreview)
ls.DbRs.AddLocalFunc("initiate_swap", appHandlers.InitiateSwap) ls.DbRs.AddLocalFunc("initiate_swap", appHandlers.InitiateSwap)
ls.DbRs.AddLocalFunc("transaction_swap_preview", appHandlers.TransactionSwapPreview)
ls.DbRs.AddLocalFunc("transaction_initiate_swap", appHandlers.TransactionInitiateSwap)
ls.DbRs.AddLocalFunc("clear_trans_type_flag", appHandlers.ClearTransactionTypeFlag)
ls.first = appHandlers.Init ls.first = appHandlers.Init
return appHandlers, nil return appHandlers, nil

View File

@ -5,19 +5,19 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "3", "input": "2",
"expectedContent": "My vouchers\n1:Select voucher\n2:Voucher details\n0:Back" "expectedContent": "My vouchers\n1:Select voucher\n2:Voucher details\n0:Back"
}, },
{ {
"input": "1", "input": "1",
"expectedContent": "Select number or symbol from your vouchers:\n1:SRF\n0:Back\n99:Quit" "expectedContent": "Select number or symbol from your vouchers:\n1SRF\n0:Back\n99:Quit"
}, },
{ {
"input": "", "input": "",
"expectedContent": "Select number or symbol from your vouchers:\n1:SRF\n0:Back\n99:Quit" "expectedContent": "Select number or symbol from your vouchers:\n1SRF\n0:Back\n99:Quit"
}, },
{ {
"input": "1", "input": "1",
@ -29,7 +29,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -38,15 +38,15 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "3", "input": "2",
"expectedContent": "My vouchers\n1:Select voucher\n2:Voucher details\n0:Back" "expectedContent": "My vouchers\n1:Select voucher\n2:Voucher details\n0:Back"
}, },
{ {
"input": "1", "input": "1",
"expectedContent": "Select number or symbol from your vouchers:\n1:SRF\n0:Back\n99:Quit" "expectedContent": "Select number or symbol from your vouchers:\n1SRF\n0:Back\n99:Quit"
}, },
{ {
"input": "SRF", "input": "SRF",
@ -58,7 +58,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -67,10 +67,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -95,7 +95,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -104,10 +104,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -136,7 +136,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -145,10 +145,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -177,7 +177,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -186,10 +186,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -210,7 +210,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -219,10 +219,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -235,7 +235,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -244,10 +244,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -280,7 +280,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -289,10 +289,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -325,7 +325,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -334,10 +334,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -382,7 +382,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -391,10 +391,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -419,7 +419,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -428,10 +428,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -460,7 +460,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -469,10 +469,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -497,7 +497,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -506,10 +506,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -534,7 +534,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -543,10 +543,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -571,7 +571,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -580,10 +580,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -604,7 +604,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -613,10 +613,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {

View File

@ -5,10 +5,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -49,7 +49,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
} }

View File

@ -5,10 +5,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -53,7 +53,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
} }

View File

@ -5,10 +5,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -45,7 +45,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
} }

View File

@ -5,10 +5,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -37,7 +37,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
} }

View File

@ -5,10 +5,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -33,7 +33,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
} }

View File

@ -5,10 +5,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -41,7 +41,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
} }

View File

@ -5,10 +5,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -61,7 +61,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
} }

View File

@ -57,7 +57,7 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "1", "input": "1",
@ -86,10 +86,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "6", "input": "4",
"expectedContent": "For more help,please call: 0757628885" "expectedContent": "For more help,please call: 0757628885"
} }
] ]
@ -99,7 +99,7 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "9", "input": "9",
@ -112,10 +112,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {

View File

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

View File

@ -1,6 +1,5 @@
LOAD reset_transaction_amount 10 LOAD reset_transaction_amount 0
RELOAD reset_transaction_amount LOAD max_amount 40
LOAD max_amount 0
RELOAD max_amount RELOAD max_amount
MAP max_amount MAP max_amount
MOUT back 0 MOUT back 0
@ -10,7 +9,7 @@ RELOAD validate_amount
CATCH api_failure flag_api_call_error 1 CATCH api_failure flag_api_call_error 1
CATCH invalid_amount flag_invalid_amount 1 CATCH invalid_amount flag_invalid_amount 1
INCMP _ 0 INCMP _ 0
LOAD get_recipient 100 LOAD get_recipient 0
LOAD get_sender 64 LOAD get_sender 64
LOAD get_amount 32 LOAD get_amount 32
INCMP transaction_pin * INCMP transaction_pin *

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,19 +0,0 @@
LOAD reset_transaction_amount 10
LOAD credit_max_amount 160
RELOAD credit_max_amount
CATCH api_failure flag_api_call_error 1
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

@ -42,15 +42,3 @@ msgstr "%s haipo kwenye %s. Tafadhali badilisha sarafu yako na ujaribu tena."
msgid "Name: %s\nSymbol: %s" msgid "Name: %s\nSymbol: %s"
msgstr "Jina: %s\nSarafu: %s" msgstr "Jina: %s\nSarafu: %s"
msgid "Only USD vouchers are allowed to 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

@ -35,4 +35,3 @@ flag,flag_account_pin_reset,41,this is set on an account when an admin triggers
flag,flag_incorrect_pool,42,this is set when the user selects an invalid pool flag,flag_incorrect_pool,42,this is set when the user selects an invalid pool
flag,flag_low_swap_amount,43,this is set when the swap max limit is less than 0.1 flag,flag_low_swap_amount,43,this is set when the swap max limit is less than 0.1
flag,flag_alias_unavailable,44,this is set when the preferred alias is not available flag,flag_alias_unavailable,44,this is set when the preferred alias is not available
flag,flag_swap_transaction,45,this is set when the transaction will involve performing a swap

Can't render this file because it has a wrong number of fields in line 34.

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 +0,0 @@
Badilisha

View File

@ -1,2 +1,2 @@
Chagua nambari au ishara ya sarafu unayotaka kupokea. Chagua nambari au ishara ya sarafu kubadilisha KWENDA:
{{.swap_to_list}} {{.swap_to_list}}

View File

@ -1,3 +0,0 @@
{{.transaction_swap_preview}}
Please enter your PIN to confirm:

View File

@ -1,13 +0,0 @@
LOAD transaction_swap_preview 0
MAP transaction_swap_preview
CATCH api_failure flag_api_call_error 1
CATCH invalid_credit_send_amount flag_invalid_amount 1
MOUT back 0
MOUT quit 9
LOAD authorize_account 6
HALT
RELOAD authorize_account
CATCH incorrect_pin flag_incorrect_pin 1
INCMP _ 0
INCMP quit 9
INCMP transaction_swap_initiated *

View File

@ -1,4 +0,0 @@
LOAD reset_incorrect_pin 6
CATCH _ flag_account_authorized 0
LOAD transaction_initiate_swap 0
HALT

View File

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

View File

@ -89,10 +89,6 @@ const (
DATA_ACTIVE_POOL_NAME DATA_ACTIVE_POOL_NAME
// Holds the active pool symbol for the swap // Holds the active pool symbol for the swap
DATA_ACTIVE_POOL_SYM DATA_ACTIVE_POOL_SYM
// Holds the send transaction type
DATA_SEND_TRANSACTION_TYPE
// Holds the recipient formatted phone number
DATA_RECIPIENT_PHONE_NUMBER
) )
const ( const (

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

@ -14,13 +14,13 @@ func TestReadSwapData(t *testing.T) {
// Test swap data // Test swap data
swapData := map[storedb.DataTyp]string{ swapData := map[storedb.DataTyp]string{
storedb.DATA_PUBLIC_KEY: publicKey, storedb.DATA_PUBLIC_KEY: publicKey,
storedb.DATA_ACTIVE_POOL_ADDRESS: "0x48a953cA5cf5298bc6f6Af3C608351f537AAcb9e", storedb.DATA_ACTIVE_POOL_ADDRESS: "0x48a953cA5cf5298bc6f6Af3C608351f537AAcb9e",
storedb.DATA_ACTIVE_SYM: "AMANI", storedb.DATA_ACTIVE_SWAP_FROM_SYM: "AMANI",
storedb.DATA_ACTIVE_DECIMAL: "6", storedb.DATA_ACTIVE_SWAP_FROM_DECIMAL: "6",
storedb.DATA_ACTIVE_ADDRESS: "0xc7B78Ac9ACB9E025C8234621FC515bC58179dEAe", storedb.DATA_ACTIVE_SWAP_FROM_ADDRESS: "0xc7B78Ac9ACB9E025C8234621FC515bC58179dEAe",
storedb.DATA_ACTIVE_SWAP_TO_SYM: "cUSD", storedb.DATA_ACTIVE_SWAP_TO_SYM: "cUSD",
storedb.DATA_ACTIVE_SWAP_TO_ADDRESS: "0x765DE816845861e75A25fCA122bb6898B8B1282a", storedb.DATA_ACTIVE_SWAP_TO_ADDRESS: "0x765DE816845861e75A25fCA122bb6898B8B1282a",
} }
// Store the data // Store the data
@ -53,16 +53,15 @@ func TestReadSwapPreviewData(t *testing.T) {
// Test swap preview data // Test swap preview data
swapPreviewData := map[storedb.DataTyp]string{ swapPreviewData := map[storedb.DataTyp]string{
storedb.DATA_TEMPORARY_VALUE: "temp", storedb.DATA_PUBLIC_KEY: publicKey,
storedb.DATA_PUBLIC_KEY: publicKey, storedb.DATA_ACTIVE_SWAP_MAX_AMOUNT: "1339482",
storedb.DATA_ACTIVE_SWAP_MAX_AMOUNT: "1339482", storedb.DATA_ACTIVE_SWAP_FROM_DECIMAL: "6",
storedb.DATA_ACTIVE_DECIMAL: "6", storedb.DATA_ACTIVE_POOL_ADDRESS: "0x48a953cA5cf5298bc6f6Af3C608351f537AAcb9e",
storedb.DATA_ACTIVE_POOL_ADDRESS: "0x48a953cA5cf5298bc6f6Af3C608351f537AAcb9e", storedb.DATA_ACTIVE_SWAP_FROM_ADDRESS: "0xc7B78Ac9ACB9E025C8234621FC515bC58179dEAe",
storedb.DATA_ACTIVE_ADDRESS: "0xc7B78Ac9ACB9E025C8234621FC515bC58179dEAe", storedb.DATA_ACTIVE_SWAP_FROM_SYM: "AMANI",
storedb.DATA_ACTIVE_SYM: "AMANI", storedb.DATA_ACTIVE_SWAP_TO_ADDRESS: "0x765DE816845861e75A25fCA122bb6898B8B1282a",
storedb.DATA_ACTIVE_SWAP_TO_ADDRESS: "0x765DE816845861e75A25fCA122bb6898B8B1282a", storedb.DATA_ACTIVE_SWAP_TO_SYM: "cUSD",
storedb.DATA_ACTIVE_SWAP_TO_SYM: "cUSD", storedb.DATA_ACTIVE_SWAP_TO_DECIMAL: "18",
storedb.DATA_ACTIVE_SWAP_TO_DECIMAL: "18",
} }
// Store the data // Store the data
@ -73,7 +72,6 @@ func TestReadSwapPreviewData(t *testing.T) {
} }
expectedResult := SwapPreviewData{ expectedResult := SwapPreviewData{
TemporaryValue: "temp",
PublicKey: "0X13242618721", PublicKey: "0X13242618721",
ActiveSwapMaxAmount: "1339482", ActiveSwapMaxAmount: "1339482",
ActiveSwapFromDecimal: "6", ActiveSwapFromDecimal: "6",

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",