From 26f6e9ea12318a92bb50fab15631d10612044158 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 14 Jul 2025 09:45:55 +0300 Subject: [PATCH] Updated the alias and balance functionality to match master --- handlers/application/alias.go | 90 +- handlers/application/balance.go | 11 +- handlers/application/menuhandler.go | 1195 --------------------------- 3 files changed, 42 insertions(+), 1254 deletions(-) diff --git a/handlers/application/alias.go b/handlers/application/alias.go index 1fffea8..f177804 100644 --- a/handlers/application/alias.go +++ b/handlers/application/alias.go @@ -25,6 +25,7 @@ func (h *MenuHandlers) RequestCustomAlias(ctx context.Context, sym string, input } flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") + flag_alias_unavailable, _ := h.flagManager.GetFlag("flag_alias_unavailable") store := h.userdataStore aliasHint, err := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE) @@ -48,9 +49,19 @@ func (h *MenuHandlers) RequestCustomAlias(ctx context.Context, sym string, input } sanitizedInput := sanitizeAliasHint(string(input)) // Check if an alias already exists - existingAlias, err := store.ReadEntry(ctx, sessionId, storedb.DATA_SUGGESTED_ALIAS) + existingAlias, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACCOUNT_ALIAS) if err == nil && len(existingAlias) > 0 { logg.InfoCtxf(ctx, "Current alias", "alias", string(existingAlias)) + + unavailable, err := h.isAliasUnavailable(ctx, sanitizedInput) + if err == nil && unavailable { + res.FlagSet = append(res.FlagSet, flag_alias_unavailable) + res.FlagReset = append(res.FlagReset, flag_api_error) + return res, nil + } + + res.FlagReset = append(res.FlagReset, flag_alias_unavailable) + // Update existing alias aliasResult, err := h.accountService.UpdateAlias(ctx, sanitizedInput, string(publicKey)) if err != nil { @@ -62,6 +73,16 @@ func (h *MenuHandlers) RequestCustomAlias(ctx context.Context, sym string, input logg.InfoCtxf(ctx, "Updated alias", "alias", alias) } else { logg.InfoCtxf(ctx, "Registering a new alias", "err", err) + + unavailable, err := h.isAliasUnavailable(ctx, sanitizedInput) + if err == nil && unavailable { + res.FlagSet = append(res.FlagSet, flag_alias_unavailable) + res.FlagReset = append(res.FlagReset, flag_api_error) + return res, nil + } + + res.FlagReset = append(res.FlagReset, flag_alias_unavailable) + // Register a new alias aliasResult, err := h.accountService.RequestAlias(ctx, string(publicKey), sanitizedInput) if err != nil { @@ -72,16 +93,18 @@ func (h *MenuHandlers) RequestCustomAlias(ctx context.Context, sym string, input res.FlagReset = append(res.FlagReset, flag_api_error) alias = aliasResult.Alias - logg.InfoCtxf(ctx, "Suggested alias", "alias", alias) + logg.InfoCtxf(ctx, "Registered alias", "alias", alias) } - //Store the returned alias,wait for user to confirm it as new account alias - logg.InfoCtxf(ctx, "Final suggested alias", "alias", alias) - err = store.WriteEntry(ctx, sessionId, storedb.DATA_SUGGESTED_ALIAS, []byte(alias)) + + //Store the new account alias + logg.InfoCtxf(ctx, "Final registered alias", "alias", alias) + err = store.WriteEntry(ctx, sessionId, storedb.DATA_ACCOUNT_ALIAS, []byte(alias)) if err != nil { - logg.ErrorCtxf(ctx, "failed to write suggested alias", "key", storedb.DATA_SUGGESTED_ALIAS, "value", alias, "error", err) + logg.ErrorCtxf(ctx, "failed to write account alias", "key", storedb.DATA_ACCOUNT_ALIAS, "value", alias, "error", err) return res, err } } + return res, nil } @@ -96,52 +119,17 @@ func sanitizeAliasHint(input string) string { return input } -// GetSuggestedAlias loads and displays the suggested alias name from the temporary value -func (h *MenuHandlers) GetSuggestedAlias(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - store := h.userdataStore +func (h *MenuHandlers) isAliasUnavailable(ctx context.Context, alias string) (bool, error) { + fqdn := fmt.Sprintf("%s.%s", alias, "sarafu.eth") + logg.InfoCtxf(ctx, "Checking if the fqdn alias is taken", "fqdn", fqdn) - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") + aliasAddress, err := h.accountService.CheckAliasAddress(ctx, fqdn) + if err != nil { + return false, err } - suggestedAlias, err := store.ReadEntry(ctx, sessionId, storedb.DATA_SUGGESTED_ALIAS) - if err != nil && len(suggestedAlias) <= 0 { - logg.ErrorCtxf(ctx, "failed to read suggested alias", "key", storedb.DATA_SUGGESTED_ALIAS, "error", err) - return res, nil + if len(aliasAddress.Address) > 0 { + return true, nil } - res.Content = string(suggestedAlias) - return res, nil -} - -// ConfirmNewAlias reads the suggested alias from the [DATA_SUGGECTED_ALIAS] key and confirms it as the new account alias. -func (h *MenuHandlers) ConfirmNewAlias(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - store := h.userdataStore - logdb := h.logDb - - flag_alias_set, _ := h.flagManager.GetFlag("flag_alias_set") - - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - newAlias, err := store.ReadEntry(ctx, sessionId, storedb.DATA_SUGGESTED_ALIAS) - if err != nil { - return res, nil - } - logg.InfoCtxf(ctx, "Confirming new alias", "alias", string(newAlias)) - err = store.WriteEntry(ctx, sessionId, storedb.DATA_ACCOUNT_ALIAS, []byte(string(newAlias))) - if err != nil { - logg.ErrorCtxf(ctx, "failed to clear DATA_ACCOUNT_ALIAS_VALUE entry with", "key", storedb.DATA_ACCOUNT_ALIAS, "value", "empty", "error", err) - return res, err - } - - err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_ACCOUNT_ALIAS, []byte(newAlias)) - if err != nil { - logg.DebugCtxf(ctx, "Failed to write account alias db log entry", "key", storedb.DATA_ACCOUNT_ALIAS, "value", newAlias) - } - - res.FlagSet = append(res.FlagSet, flag_alias_set) - return res, nil + + return false, nil } diff --git a/handlers/application/balance.go b/handlers/application/balance.go index f36a6a3..f5c0a0b 100644 --- a/handlers/application/balance.go +++ b/handlers/application/balance.go @@ -3,7 +3,6 @@ package application import ( "context" "fmt" - "strings" "git.defalsify.org/vise.git/db" "git.defalsify.org/vise.git/resource" @@ -18,7 +17,6 @@ func (h *MenuHandlers) CheckBalance(ctx context.Context, sym string, input []byt var ( res resource.Result err error - alias string content string ) @@ -53,11 +51,8 @@ func (h *MenuHandlers) CheckBalance(ctx context.Context, sym string, input []byt logg.ErrorCtxf(ctx, "failed to read account alias entry with", "key", storedb.DATA_ACCOUNT_ALIAS, "error", err) return res, err } - } else { - alias = strings.Split(string(accAlias), ".")[0] } - - content, err = loadUserContent(ctx, string(activeSym), string(activeBal), alias) + content, err = loadUserContent(ctx, string(activeSym), string(activeBal), string(accAlias)) if err != nil { return res, err } @@ -66,7 +61,7 @@ func (h *MenuHandlers) CheckBalance(ctx context.Context, sym string, input []byt return res, nil } -// loadUserContent loads the main user content in the main menu: the alias,balance associated with active voucher +// loadUserContent loads the main user content in the main menu: the alias, balance and active symbol associated with active voucher func loadUserContent(ctx context.Context, activeSym string, balance string, alias string) (string, error) { var content string @@ -83,7 +78,7 @@ func loadUserContent(ctx context.Context, activeSym string, balance string, alia // format the final output balStr := fmt.Sprintf("%s %s", formattedAmount, activeSym) if alias != "" { - content = l.Get("%s balance: %s\n", alias, balStr) + content = l.Get("%s\nBalance: %s\n", alias, balStr) } else { content = l.Get("Balance: %s\n", balStr) } diff --git a/handlers/application/menuhandler.go b/handlers/application/menuhandler.go index 696d230..4c7b4ec 100644 --- a/handlers/application/menuhandler.go +++ b/handlers/application/menuhandler.go @@ -253,1201 +253,6 @@ func (h *MenuHandlers) ShowBlockedAccount(ctx context.Context, sym string, input return res, nil } -// loadUserContent loads the main user content in the main menu: the alias, balance and active symbol associated with active voucher -func loadUserContent(ctx context.Context, activeSym string, balance string, alias string) (string, error) { - var content string - - code := codeFromCtx(ctx) - l := gotext.NewLocale(translationDir, code) - l.AddDomain("default") - - // Format the balance to 2 decimal places or default to 0.00 - formattedAmount, err := store.TruncateDecimalString(balance, 2) - if err != nil { - formattedAmount = "0.00" - } - - // format the final output - balStr := fmt.Sprintf("%s %s", formattedAmount, activeSym) - if alias != "" { - content = l.Get("%s\nBalance: %s\n", alias, balStr) - } else { - content = l.Get("Balance: %s\n", balStr) - } - return content, nil -} - -// CheckBalance retrieves the balance of the active voucher and sets -// the balance as the result content. -func (h *MenuHandlers) CheckBalance(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var ( - res resource.Result - err error - content string - ) - - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - - store := h.userdataStore - - // get the active sym and active balance - activeSym, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM) - if err != nil { - logg.InfoCtxf(ctx, "could not find the activeSym in checkBalance:", "err", err) - if !db.IsNotFound(err) { - logg.ErrorCtxf(ctx, "failed to read activeSym entry with", "key", storedb.DATA_ACTIVE_SYM, "error", err) - return res, err - } - } - - activeBal, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_BAL) - if err != nil { - if !db.IsNotFound(err) { - logg.ErrorCtxf(ctx, "failed to read activeBal entry with", "key", storedb.DATA_ACTIVE_BAL, "error", err) - return res, err - } - } - - accAlias, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACCOUNT_ALIAS) - if err != nil { - if !db.IsNotFound(err) { - logg.ErrorCtxf(ctx, "failed to read account alias entry with", "key", storedb.DATA_ACCOUNT_ALIAS, "error", err) - return res, err - } - } - content, err = loadUserContent(ctx, string(activeSym), string(activeBal), string(accAlias)) - if err != nil { - return res, err - } - res.Content = content - - return res, nil -} - -// FetchCommunityBalance retrieves and displays the balance for community accounts in user's preferred language. -func (h *MenuHandlers) FetchCommunityBalance(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - // retrieve the language code from the context - code := codeFromCtx(ctx) - // Initialize the localization system with the appropriate translation directory - l := gotext.NewLocale(translationDir, code) - l.AddDomain("default") - //TODO: - //Check if the address is a community account,if then,get the actual balance - res.Content = l.Get("Community Balance: 0.00") - return res, nil -} - -// 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) { - var res resource.Result - var AliasAddressResult string - var AliasAddress *models.AliasAddress - store := h.userdataStore - - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - 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 - recipient := strings.ReplaceAll(string(input), " ", "") - - if recipient != "0" { - recipientType, err := identity.CheckRecipient(recipient) - if err != nil { - // Invalid recipient format (not a phone number, address, or valid alias format) - res.FlagSet = append(res.FlagSet, flag_invalid_recipient) - res.Content = 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": - // format the phone number - formattedNumber, err := phone.FormatPhoneNumber(recipient) - 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 { - //Perform a search for each search domain,break on first match - for _, domain := range config.SearchDomains() { - 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 - } - } - } - } - - return res, nil -} - -// TransactionReset resets the previous transaction data (Recipient and Amount) -// as well as the invalid flags. -func (h *MenuHandlers) TransactionReset(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_invalid_recipient, _ := h.flagManager.GetFlag("flag_invalid_recipient") - flag_invalid_recipient_with_invite, _ := h.flagManager.GetFlag("flag_invalid_recipient_with_invite") - store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, storedb.DATA_AMOUNT, []byte("")) - if err != nil { - return res, nil - } - - err = store.WriteEntry(ctx, sessionId, storedb.DATA_RECIPIENT, []byte("")) - if err != nil { - return res, nil - } - - res.FlagReset = append(res.FlagReset, flag_invalid_recipient, flag_invalid_recipient_with_invite) - - return res, nil -} - -// InviteValidRecipient sends an invitation to the valid phone number. -func (h *MenuHandlers) InviteValidRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - store := h.userdataStore - smsservice := h.smsService - - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - - code := codeFromCtx(ctx) - l := gotext.NewLocale(translationDir, code) - l.AddDomain("default") - - recipient, err := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE) - if err != nil { - logg.ErrorCtxf(ctx, "Failed to read invalid recipient info", "error", err) - return res, err - } - - if !phone.IsValidPhoneNumber(string(recipient)) { - logg.InfoCtxf(ctx, "corrupted recipient", "key", storedb.DATA_TEMPORARY_VALUE, "recipient", recipient) - return res, nil - } - - _, err = smsservice.Accountservice.SendUpsellSMS(ctx, sessionId, string(recipient)) - if err != nil { - res.Content = l.Get("Your invite request for %s to Sarafu Network failed. Please try again later.", string(recipient)) - return res, nil - } - res.Content = l.Get("Your invitation to %s to join Sarafu Network has been sent.", string(recipient)) - return res, nil -} - -// ResetTransactionAmount resets the transaction amount and invalid flag. -func (h *MenuHandlers) ResetTransactionAmount(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_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount") - store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, storedb.DATA_AMOUNT, []byte("")) - if err != nil { - return res, nil - } - - res.FlagReset = append(res.FlagReset, flag_invalid_amount) - - return res, nil -} - -// MaxAmount gets the current balance from the API and sets it as -// the result content. -func (h *MenuHandlers) MaxAmount(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") - } - store := h.userdataStore - - activeBal, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_BAL) - if err != nil { - logg.ErrorCtxf(ctx, "failed to read activeBal entry with", "key", storedb.DATA_ACTIVE_BAL, "error", err) - return res, err - } - - res.Content = string(activeBal) - - return res, nil -} - -// ValidateAmount ensures that the given input is a valid amount and that -// it is not more than the current balance. -func (h *MenuHandlers) ValidateAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount") - userStore := h.userdataStore - - var balanceValue float64 - - // retrieve the active balance - activeBal, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_BAL) - if err != nil { - logg.ErrorCtxf(ctx, "failed to read activeBal entry with", "key", storedb.DATA_ACTIVE_BAL, "error", err) - return res, err - } - balanceValue, err = strconv.ParseFloat(string(activeBal), 64) - if err != nil { - logg.ErrorCtxf(ctx, "Failed to convert the activeBal to a float", "error", err) - return res, err - } - - // Extract numeric part from the input amount - amountStr := strings.TrimSpace(string(input)) - inputAmount, err := strconv.ParseFloat(amountStr, 64) - if err != nil { - res.FlagSet = append(res.FlagSet, flag_invalid_amount) - res.Content = amountStr - return res, nil - } - - if inputAmount > balanceValue { - res.FlagSet = append(res.FlagSet, flag_invalid_amount) - res.Content = amountStr - return res, nil - } - - // Format the amount to 2 decimal places before saving (truncated) - formattedAmount, err := store.TruncateDecimalString(amountStr, 2) - if err != nil { - res.FlagSet = append(res.FlagSet, flag_invalid_amount) - res.Content = amountStr - return res, nil - } - - err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_AMOUNT, []byte(formattedAmount)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write amount entry with", "key", storedb.DATA_AMOUNT, "value", formattedAmount, "error", err) - return res, err - } - - res.Content = formattedAmount - return res, nil -} - -// GetRecipient returns the transaction recipient phone number from the gdbm. -func (h *MenuHandlers) GetRecipient(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") - } - store := h.userdataStore - recipient, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE) - if len(recipient) == 0 { - logg.ErrorCtxf(ctx, "recipient is empty", "key", storedb.DATA_TEMPORARY_VALUE) - return res, fmt.Errorf("Data error encountered") - } - - res.Content = string(recipient) - - return res, nil -} - -// RetrieveBlockedNumber gets the current number during the pin reset for other's is in progress. -func (h *MenuHandlers) RetrieveBlockedNumber(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") - } - store := h.userdataStore - blockedNumber, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_BLOCKED_NUMBER) - - res.Content = string(blockedNumber) - - return res, nil -} - -// GetSender returns the sessionId (phoneNumber). -func (h *MenuHandlers) GetSender(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") - } - - res.Content = sessionId - - return res, nil -} - -// GetAmount retrieves the amount from teh Gdbm Db. -func (h *MenuHandlers) GetAmount(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") - } - store := h.userdataStore - - // retrieve the active symbol - activeSym, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM) - if err != nil { - logg.ErrorCtxf(ctx, "failed to read activeSym entry with", "key", storedb.DATA_ACTIVE_SYM, "error", err) - return res, err - } - - amount, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_AMOUNT) - - res.Content = fmt.Sprintf("%s %s", string(amount), string(activeSym)) - - return res, nil -} - -// InitiateTransaction calls the TokenTransfer and returns a confirmation based on the result. -func (h *MenuHandlers) InitiateTransaction(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - var err error - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - - flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") - - code := codeFromCtx(ctx) - l := gotext.NewLocale(translationDir, code) - l.AddDomain("default") - - data, err := store.ReadTransactionData(ctx, h.userdataStore, sessionId) - if err != nil { - return res, err - } - - finalAmountStr, err := store.ParseAndScaleAmount(data.Amount, data.ActiveDecimal) - if err != nil { - return res, err - } - - // Call TokenTransfer - r, err := h.accountService.TokenTransfer(ctx, finalAmountStr, data.PublicKey, data.Recipient, data.ActiveAddress) - if err != nil { - flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") - res.FlagSet = append(res.FlagSet, flag_api_error) - res.Content = l.Get("Your request failed. Please try again later.") - logg.ErrorCtxf(ctx, "failed on TokenTransfer", "error", err) - return res, nil - } - - trackingId := r.TrackingId - logg.InfoCtxf(ctx, "TokenTransfer", "trackingId", trackingId) - - res.Content = l.Get( - "Your request has been sent. %s will receive %s %s from %s.", - data.TemporaryValue, - data.Amount, - data.ActiveSym, - sessionId, - ) - - res.FlagReset = append(res.FlagReset, flag_account_authorized) - return res, nil -} - -// ManageVouchers retrieves the token holdings from the API using the "PublicKey" and -// 1. sets the first as the default voucher if no active voucher is set. -// 2. Stores list of vouchers -// 3. updates the balance of the active voucher -func (h *MenuHandlers) ManageVouchers(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - userStore := h.userdataStore - logdb := h.logDb - - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - - flag_no_active_voucher, _ := h.flagManager.GetFlag("flag_no_active_voucher") - flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") - - publicKey, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY) - if err != nil { - logg.ErrorCtxf(ctx, "failed to read publicKey entry", "key", storedb.DATA_PUBLIC_KEY, "error", err) - return res, err - } - - // Fetch vouchers from API - vouchersResp, err := h.accountService.FetchVouchers(ctx, string(publicKey)) - if err != nil { - res.FlagSet = append(res.FlagSet, flag_api_error) - return res, nil - } - res.FlagReset = append(res.FlagReset, flag_api_error) - - if len(vouchersResp) == 0 { - res.FlagSet = append(res.FlagSet, flag_no_active_voucher) - return res, nil - } - - res.FlagReset = append(res.FlagReset, flag_no_active_voucher) - - // Check if user has an active voucher with proper error handling - activeSym, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM) - if err != nil { - if db.IsNotFound(err) { - // No active voucher, set the first one as default - firstVoucher := vouchersResp[0] - defaultSym := firstVoucher.TokenSymbol - defaultBal := firstVoucher.Balance - defaultDec := firstVoucher.TokenDecimals - defaultAddr := firstVoucher.TokenAddress - - // Scale down the balance - scaledBalance := store.ScaleDownBalance(defaultBal, defaultDec) - - firstVoucherMap := map[storedb.DataTyp]string{ - storedb.DATA_ACTIVE_SYM: defaultSym, - storedb.DATA_ACTIVE_BAL: scaledBalance, - storedb.DATA_ACTIVE_DECIMAL: defaultDec, - storedb.DATA_ACTIVE_ADDRESS: defaultAddr, - } - - for key, value := range firstVoucherMap { - if err := userStore.WriteEntry(ctx, sessionId, key, []byte(value)); err != nil { - logg.ErrorCtxf(ctx, "Failed to write active voucher data", "key", key, "error", err) - return res, err - } - err = logdb.WriteLogEntry(ctx, sessionId, key, []byte(value)) - if err != nil { - logg.DebugCtxf(ctx, "Failed to write voucher db log entry", "key", key, "value", value) - } - } - - logg.InfoCtxf(ctx, "Default voucher set", "symbol", defaultSym, "balance", defaultBal, "decimals", defaultDec, "address", defaultAddr) - } else { - logg.ErrorCtxf(ctx, "failed to read activeSym entry with", "key", storedb.DATA_ACTIVE_SYM, "error", err) - return res, err - } - } else { - // Update active voucher balance - activeSymStr := string(activeSym) - - // Find the matching voucher data - var activeData *dataserviceapi.TokenHoldings - for _, voucher := range vouchersResp { - if voucher.TokenSymbol == activeSymStr { - activeData = &voucher - break - } - } - - if activeData == nil { - logg.ErrorCtxf(ctx, "activeSym not found in vouchers, setting the first voucher as the default", "activeSym", activeSymStr) - firstVoucher := vouchersResp[0] - activeData = &firstVoucher - } - - // Scale down the balance - scaledBalance := store.ScaleDownBalance(activeData.Balance, activeData.TokenDecimals) - - // Update the balance field with the scaled value - activeData.Balance = scaledBalance - - // Pass the matching voucher data to UpdateVoucherData - if err := store.UpdateVoucherData(ctx, h.userdataStore, sessionId, activeData); err != nil { - logg.ErrorCtxf(ctx, "failed on UpdateVoucherData", "error", err) - return res, err - } - } - - // Store all voucher data - data := store.ProcessVouchers(vouchersResp) - dataMap := map[storedb.DataTyp]string{ - storedb.DATA_VOUCHER_SYMBOLS: data.Symbols, - storedb.DATA_VOUCHER_BALANCES: data.Balances, - storedb.DATA_VOUCHER_DECIMALS: data.Decimals, - storedb.DATA_VOUCHER_ADDRESSES: data.Addresses, - } - - // Write data entries - for key, value := range dataMap { - if err := userStore.WriteEntry(ctx, sessionId, key, []byte(value)); err != nil { - logg.ErrorCtxf(ctx, "Failed to write data entry for sessionId: %s", sessionId, "key", key, "error", err) - continue - } - } - - return res, nil -} - -// GetVoucherList fetches the list of vouchers and formats them. -func (h *MenuHandlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - - userStore := h.userdataStore - - // Read vouchers from the store - voucherData, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_VOUCHER_SYMBOLS) - logg.InfoCtxf(ctx, "reading voucherData in GetVoucherList", "sessionId", sessionId, "key", storedb.DATA_VOUCHER_SYMBOLS, "voucherData", voucherData) - if err != nil { - logg.ErrorCtxf(ctx, "failed to read voucherData entires with", "key", storedb.DATA_VOUCHER_SYMBOLS, "error", err) - return res, err - } - - voucherBalances, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_VOUCHER_BALANCES) - logg.InfoCtxf(ctx, "reading voucherBalances in GetVoucherList", "sessionId", sessionId, "key", storedb.DATA_VOUCHER_BALANCES, "voucherBalances", voucherBalances) - if err != nil { - logg.ErrorCtxf(ctx, "failed to read voucherData entires with", "key", storedb.DATA_VOUCHER_BALANCES, "error", err) - return res, err - } - - formattedVoucherList := store.FormatVoucherList(ctx, string(voucherData), string(voucherBalances)) - finalOutput := strings.Join(formattedVoucherList, "\n") - - logg.InfoCtxf(ctx, "final output for GetVoucherList", "sessionId", sessionId, "finalOutput", finalOutput) - - res.Content = finalOutput - - return res, nil -} - -// ViewVoucher retrieves the token holding and balance from the subprefixDB -// and displays it to the user for them to select it. -func (h *MenuHandlers) ViewVoucher(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") - } - - code := codeFromCtx(ctx) - l := gotext.NewLocale(translationDir, code) - l.AddDomain("default") - - flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher") - - inputStr := string(input) - - metadata, err := store.GetVoucherData(ctx, h.userdataStore, sessionId, inputStr) - if err != nil { - return res, fmt.Errorf("failed to retrieve voucher data: %v", err) - } - - if metadata == nil { - res.FlagSet = append(res.FlagSet, flag_incorrect_voucher) - return res, nil - } - - if err := store.StoreTemporaryVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil { - logg.ErrorCtxf(ctx, "failed on StoreTemporaryVoucher", "error", err) - return res, err - } - - res.FlagReset = append(res.FlagReset, flag_incorrect_voucher) - res.Content = l.Get("Symbol: %s\nBalance: %s", metadata.TokenSymbol, metadata.Balance) - - return res, nil -} - -// SetVoucher retrieves the temp voucher data and sets it as the active data. -func (h *MenuHandlers) SetVoucher(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") - } - - // Get temporary data - tempData, err := store.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId) - if err != nil { - logg.ErrorCtxf(ctx, "failed on GetTemporaryVoucherData", "error", err) - return res, err - } - - // Set as active and clear temporary data - if err := store.UpdateVoucherData(ctx, h.userdataStore, sessionId, tempData); err != nil { - logg.ErrorCtxf(ctx, "failed on UpdateVoucherData", "error", err) - return res, err - } - - res.Content = tempData.TokenSymbol - return res, nil -} - -// GetVoucherDetails retrieves the voucher details. -func (h *MenuHandlers) GetVoucherDetails(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - store := h.userdataStore - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") - - // get the active address - activeAddress, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_ADDRESS) - if err != nil { - logg.ErrorCtxf(ctx, "failed to read activeAddress entry with", "key", storedb.DATA_ACTIVE_ADDRESS, "error", err) - return res, err - } - - // use the voucher contract address to get the data from the API - voucherData, err := h.accountService.VoucherData(ctx, string(activeAddress)) - if err != nil { - res.FlagSet = append(res.FlagSet, flag_api_error) - return res, nil - } - res.FlagReset = append(res.FlagReset, flag_api_error) - - res.Content = fmt.Sprintf( - "Name: %s\nSymbol: %s\nCommodity: %s\nLocation: %s", voucherData.TokenName, voucherData.TokenSymbol, voucherData.TokenCommodity, voucherData.TokenLocation, - ) - - return res, nil -} - -// GetDefaultPool returns the current user's Pool. If none is set, it returns the default config pool. -func (h *MenuHandlers) GetDefaultPool(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - - userStore := h.userdataStore - activePoolSym, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_POOL_SYM) - if err != nil { - if db.IsNotFound(err) { - // set the default as the response - res.Content = config.DefaultPoolSymbol() - return res, nil - } - - logg.ErrorCtxf(ctx, "failed to read the activePoolSym entry with", "key", storedb.DATA_ACTIVE_POOL_SYM, "error", err) - return res, err - } - - res.Content = string(activePoolSym) - - return res, nil -} - -// ViewPool retrieves the pool details from the user store -// and displays it to the user for them to select it. -func (h *MenuHandlers) ViewPool(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") - } - - code := codeFromCtx(ctx) - l := gotext.NewLocale(translationDir, code) - l.AddDomain("default") - - flag_incorrect_pool, _ := h.flagManager.GetFlag("flag_incorrect_pool") - - inputStr := string(input) - - poolData, err := store.GetPoolData(ctx, h.userdataStore, sessionId, inputStr) - if err != nil { - return res, fmt.Errorf("failed to retrieve pool data: %v", err) - } - - if poolData == nil { - flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") - - // no match found. Call the API using the inputStr as the symbol - poolResp, err := h.accountService.RetrievePoolDetails(ctx, inputStr) - if err != nil { - res.FlagSet = append(res.FlagSet, flag_api_error) - return res, nil - } - - if len(poolResp.PoolSymbol) == 0 { - // If the API does not return the data, set the flag - res.FlagSet = append(res.FlagSet, flag_incorrect_pool) - return res, nil - } - - poolData = poolResp - } - - if err := store.StoreTemporaryPool(ctx, h.userdataStore, sessionId, poolData); err != nil { - logg.ErrorCtxf(ctx, "failed on StoreTemporaryPool", "error", err) - return res, err - } - - res.FlagReset = append(res.FlagReset, flag_incorrect_pool) - res.Content = l.Get("Name: %s\nSymbol: %s", poolData.PoolName, poolData.PoolSymbol) - - return res, nil -} - -// SetPool retrieves the temp pool data and sets it as the active data. -func (h *MenuHandlers) SetPool(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") - } - - // Get temporary data - tempData, err := store.GetTemporaryPoolData(ctx, h.userdataStore, sessionId) - if err != nil { - logg.ErrorCtxf(ctx, "failed on GetTemporaryPoolData", "error", err) - return res, err - } - - // Set as active and clear temporary data - if err := store.UpdatePoolData(ctx, h.userdataStore, sessionId, tempData); err != nil { - logg.ErrorCtxf(ctx, "failed on UpdatePoolData", "error", err) - return res, err - } - - res.Content = tempData.PoolSymbol - return res, nil -} - -// CheckTransactions retrieves the transactions from the API using the "PublicKey" and stores to prefixDb. -func (h *MenuHandlers) CheckTransactions(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - - flag_no_transfers, _ := h.flagManager.GetFlag("flag_no_transfers") - flag_api_error, _ := h.flagManager.GetFlag("flag_api_error") - - userStore := h.userdataStore - logdb := h.logDb - publicKey, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY) - if err != nil { - logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", storedb.DATA_PUBLIC_KEY, "error", err) - return res, err - } - - // Fetch transactions from the API using the public key - transactionsResp, err := h.accountService.FetchTransactions(ctx, string(publicKey)) - if err != nil { - res.FlagSet = append(res.FlagSet, flag_api_error) - logg.ErrorCtxf(ctx, "failed on FetchTransactions", "error", err) - return res, err - } - res.FlagReset = append(res.FlagReset, flag_api_error) - - // Return if there are no transactions - if len(transactionsResp) == 0 { - res.FlagSet = append(res.FlagSet, flag_no_transfers) - return res, nil - } - - data := store.ProcessTransfers(transactionsResp) - - // Store all transaction data - dataMap := map[storedb.DataTyp]string{ - storedb.DATA_TX_SENDERS: data.Senders, - storedb.DATA_TX_RECIPIENTS: data.Recipients, - storedb.DATA_TX_VALUES: data.TransferValues, - storedb.DATA_TX_ADDRESSES: data.Addresses, - storedb.DATA_TX_HASHES: data.TxHashes, - storedb.DATA_TX_DATES: data.Dates, - storedb.DATA_TX_SYMBOLS: data.Symbols, - storedb.DATA_TX_DECIMALS: data.Decimals, - } - - for key, value := range dataMap { - if err := h.prefixDb.Put(ctx, []byte(storedb.ToBytes(key)), []byte(value)); err != nil { - logg.ErrorCtxf(ctx, "failed to write to prefixDb", "error", err) - return res, err - } - err = logdb.WriteLogEntry(ctx, sessionId, key, []byte(value)) - if err != nil { - logg.DebugCtxf(ctx, "Failed to write tx db log entry", "key", key, "value", value) - } - } - - res.FlagReset = append(res.FlagReset, flag_no_transfers) - - return res, nil -} - -// GetTransactionsList fetches the list of transactions and formats them. -func (h *MenuHandlers) GetTransactionsList(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - - userStore := h.userdataStore - publicKey, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY) - if err != nil { - logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", storedb.DATA_PUBLIC_KEY, "error", err) - return res, err - } - - // Read transactions from the store and format them - TransactionSenders, err := h.prefixDb.Get(ctx, storedb.ToBytes(storedb.DATA_TX_SENDERS)) - if err != nil { - logg.ErrorCtxf(ctx, "Failed to read the TransactionSenders from prefixDb", "error", err) - return res, err - } - TransactionSyms, err := h.prefixDb.Get(ctx, storedb.ToBytes(storedb.DATA_TX_SYMBOLS)) - if err != nil { - logg.ErrorCtxf(ctx, "Failed to read the TransactionSyms from prefixDb", "error", err) - return res, err - } - TransactionValues, err := h.prefixDb.Get(ctx, storedb.ToBytes(storedb.DATA_TX_VALUES)) - if err != nil { - logg.ErrorCtxf(ctx, "Failed to read the TransactionValues from prefixDb", "error", err) - return res, err - } - TransactionDates, err := h.prefixDb.Get(ctx, storedb.ToBytes(storedb.DATA_TX_DATES)) - if err != nil { - logg.ErrorCtxf(ctx, "Failed to read the TransactionDates from prefixDb", "error", err) - return res, err - } - - // Parse the data - senders := strings.Split(string(TransactionSenders), "\n") - syms := strings.Split(string(TransactionSyms), "\n") - values := strings.Split(string(TransactionValues), "\n") - dates := strings.Split(string(TransactionDates), "\n") - - var formattedTransactions []string - for i := 0; i < len(senders); i++ { - sender := strings.TrimSpace(senders[i]) - sym := strings.TrimSpace(syms[i]) - value := strings.TrimSpace(values[i]) - date := strings.Split(strings.TrimSpace(dates[i]), " ")[0] - - status := "Received" - if sender == string(publicKey) { - status = "Sent" - } - - // Use the ReplaceSeparator function for the menu separator - transactionLine := fmt.Sprintf("%d%s%s %s %s %s", i+1, h.ReplaceSeparatorFunc(":"), status, value, sym, date) - formattedTransactions = append(formattedTransactions, transactionLine) - } - - res.Content = strings.Join(formattedTransactions, "\n") - - return res, nil -} - -// ViewTransactionStatement retrieves the transaction statement -// and displays it to the user. -func (h *MenuHandlers) ViewTransactionStatement(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - userStore := h.userdataStore - publicKey, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY) - if err != nil { - logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", storedb.DATA_PUBLIC_KEY, "error", err) - return res, err - } - - flag_incorrect_statement, _ := h.flagManager.GetFlag("flag_incorrect_statement") - - inputStr := string(input) - if inputStr == "0" || inputStr == "99" || inputStr == "11" || inputStr == "22" { - res.FlagReset = append(res.FlagReset, flag_incorrect_statement) - return res, nil - } - - // Convert input string to integer - index, err := strconv.Atoi(strings.TrimSpace(inputStr)) - if err != nil { - return res, fmt.Errorf("invalid input: must be a number between 1 and 10") - } - - if index < 1 || index > 10 { - return res, fmt.Errorf("invalid input: index must be between 1 and 10") - } - - statement, err := store.GetTransferData(ctx, h.prefixDb, string(publicKey), index) - if err != nil { - return res, fmt.Errorf("failed to retrieve transfer data: %v", err) - } - - if statement == "" { - res.FlagSet = append(res.FlagSet, flag_incorrect_statement) - return res, nil - } - - res.FlagReset = append(res.FlagReset, flag_incorrect_statement) - res.Content = statement - - return res, nil -} - -// persistInitialLanguageCode receives an initial language code and persists it to the store -func (h *MenuHandlers) persistInitialLanguageCode(ctx context.Context, sessionId string, code string) error { - store := h.userdataStore - _, err := store.ReadEntry(ctx, sessionId, storedb.DATA_INITIAL_LANGUAGE_CODE) - if err == nil { - return nil - } - if !db.IsNotFound(err) { - return err - } - err = store.WriteEntry(ctx, sessionId, storedb.DATA_INITIAL_LANGUAGE_CODE, []byte(code)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to persist initial language code", "key", storedb.DATA_INITIAL_LANGUAGE_CODE, "value", code, "error", err) - return err - } - return nil -} - -// persistLanguageCode persists the selected ISO 639 language code -func (h *MenuHandlers) persistLanguageCode(ctx context.Context, code string) error { - store := h.userdataStore - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return fmt.Errorf("missing session") - } - err := store.WriteEntry(ctx, sessionId, storedb.DATA_SELECTED_LANGUAGE_CODE, []byte(code)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to persist language code", "key", storedb.DATA_SELECTED_LANGUAGE_CODE, "value", code, "error", err) - return err - } - return h.persistInitialLanguageCode(ctx, sessionId, code) -} - -// RequestCustomAlias requests an ENS based alias name based on a user's input,then saves it as temporary value -func (h *MenuHandlers) RequestCustomAlias(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - var alias string - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - if string(input) == "0" { - return res, nil - } - - flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") - flag_alias_unavailable, _ := h.flagManager.GetFlag("flag_alias_unavailable") - - store := h.userdataStore - aliasHint, err := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE) - if err != nil { - if db.IsNotFound(err) { - return res, nil - } - return res, err - } - //Ensures that the call doesn't happen twice for the same alias hint - if !bytes.Equal(aliasHint, input) { - err = store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(string(input))) - if err != nil { - return res, err - } - publicKey, err := store.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY) - if err != nil { - if db.IsNotFound(err) { - return res, nil - } - } - sanitizedInput := sanitizeAliasHint(string(input)) - // Check if an alias already exists - existingAlias, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACCOUNT_ALIAS) - if err == nil && len(existingAlias) > 0 { - logg.InfoCtxf(ctx, "Current alias", "alias", string(existingAlias)) - - unavailable, err := h.isAliasUnavailable(ctx, sanitizedInput) - if err == nil && unavailable { - res.FlagSet = append(res.FlagSet, flag_alias_unavailable) - res.FlagReset = append(res.FlagReset, flag_api_error) - return res, nil - } - - res.FlagReset = append(res.FlagReset, flag_alias_unavailable) - - // Update existing alias - aliasResult, err := h.accountService.UpdateAlias(ctx, sanitizedInput, string(publicKey)) - if err != nil { - res.FlagSet = append(res.FlagSet, flag_api_error) - logg.ErrorCtxf(ctx, "failed to update alias", "alias", sanitizedInput, "error", err) - return res, nil - } - alias = aliasResult.Alias - logg.InfoCtxf(ctx, "Updated alias", "alias", alias) - } else { - logg.InfoCtxf(ctx, "Registering a new alias", "err", err) - - unavailable, err := h.isAliasUnavailable(ctx, sanitizedInput) - if err == nil && unavailable { - res.FlagSet = append(res.FlagSet, flag_alias_unavailable) - res.FlagReset = append(res.FlagReset, flag_api_error) - return res, nil - } - - res.FlagReset = append(res.FlagReset, flag_alias_unavailable) - - // Register a new alias - aliasResult, err := h.accountService.RequestAlias(ctx, string(publicKey), sanitizedInput) - if err != nil { - res.FlagSet = append(res.FlagSet, flag_api_error) - logg.ErrorCtxf(ctx, "failed to retrieve alias", "alias", sanitizedInput, "error_alias_request", err) - return res, nil - } - res.FlagReset = append(res.FlagReset, flag_api_error) - - alias = aliasResult.Alias - logg.InfoCtxf(ctx, "Registered alias", "alias", alias) - } - - //Store the new account alias - logg.InfoCtxf(ctx, "Final registered alias", "alias", alias) - err = store.WriteEntry(ctx, sessionId, storedb.DATA_ACCOUNT_ALIAS, []byte(alias)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write account alias", "key", storedb.DATA_ACCOUNT_ALIAS, "value", alias, "error", err) - return res, err - } - } - - return res, nil -} - -func sanitizeAliasHint(input string) string { - for i, r := range input { - // Check if the character is a special character (non-alphanumeric) - if !unicode.IsLetter(r) && !unicode.IsNumber(r) { - return input[:i] - } - } - // If no special character is found, return the whole input - return input -} - -func (h *MenuHandlers) isAliasUnavailable(ctx context.Context, alias string) (bool, error) { - fqdn := fmt.Sprintf("%s.%s", alias, "sarafu.eth") - logg.InfoCtxf(ctx, "Checking if the fqdn alias is taken", "fqdn", fqdn) - - aliasAddress, err := h.accountService.CheckAliasAddress(ctx, fqdn) - if err != nil { - return false, err - } - if len(aliasAddress.Address) > 0 { - return true, nil - } - - return false, nil -} - // ClearTemporaryValue empties the DATA_TEMPORARY_VALUE at the main menu to prevent // previously stored data from being accessed func (h *MenuHandlers) ClearTemporaryValue(ctx context.Context, sym string, input []byte) (resource.Result, error) {