From 42dadb5eea68d536ce243ce162ca844ecde125af Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 9 Jun 2025 13:38:19 +0200 Subject: [PATCH] add-pool-selection (#79) - Add pool selection under "Select vouchers" node - Use the set pool contract address in the swap, or fall back to the default Reviewed-on: https://git.grassecon.net/grassrootseconomics/sarafu-vise/pulls/79 Co-authored-by: alfred-mk Co-committed-by: alfred-mk --- go.mod | 2 +- go.sum | 4 + handlers/application/menuhandler.go | 136 +++++++++++++++++--- handlers/local.go | 4 + services/registration/locale/swa/default.po | 5 +- services/registration/my_vouchers.vis | 3 + services/registration/pool_set | 1 + services/registration/pool_set.vis | 10 ++ services/registration/pool_set_swa | 1 + services/registration/select_pool | 3 + services/registration/select_pool.vis | 20 +++ services/registration/select_pool_menu | 1 + services/registration/select_pool_menu_swa | 1 + services/registration/select_pool_swa | 3 + services/registration/view_pool | 2 + services/registration/view_pool.vis | 10 ++ services/registration/view_pool_swa | 2 + services/registration/voucher_set.vis | 1 - store/db/db.go | 4 + store/pools.go | 49 +++++++ 20 files changed, 244 insertions(+), 18 deletions(-) create mode 100644 services/registration/pool_set create mode 100644 services/registration/pool_set.vis create mode 100644 services/registration/pool_set_swa create mode 100644 services/registration/select_pool create mode 100644 services/registration/select_pool.vis create mode 100644 services/registration/select_pool_menu create mode 100644 services/registration/select_pool_menu_swa create mode 100644 services/registration/select_pool_swa create mode 100644 services/registration/view_pool create mode 100644 services/registration/view_pool.vis create mode 100644 services/registration/view_pool_swa diff --git a/go.mod b/go.mod index 7dc407d..3d137e7 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.23.4 require ( git.defalsify.org/vise.git v0.3.2-0.20250507135825-a170e8a79da0 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.20250522123108-24224e553de5 + git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250606194235-b5ccaea57560 git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306 git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694 github.com/alecthomas/assert/v2 v2.2.2 diff --git a/go.sum b/go.sum index bd39495..2dd77f1 100644 --- a/go.sum +++ b/go.sum @@ -22,6 +22,10 @@ git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250521141246- git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250521141246-6c3719e3b652/go.mod h1:wKHPy1mpOCr9ahkRltwm1yi9qH/3m9xb8hMCX5C0L1o= git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250522123108-24224e553de5 h1:7gVnkpybzg5lC7C8Rl4dejTbmBVpu5xfMMfda+d498U= git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250522123108-24224e553de5/go.mod h1:wKHPy1mpOCr9ahkRltwm1yi9qH/3m9xb8hMCX5C0L1o= +git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250605174108-bf830e92dea2 h1:8cxGb7lSoNGJxjauIRGskp//EqisuOOZPoPj4oDwswE= +git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250605174108-bf830e92dea2/go.mod h1:wKHPy1mpOCr9ahkRltwm1yi9qH/3m9xb8hMCX5C0L1o= +git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250606194235-b5ccaea57560 h1:14QVGEgLdl1LyVS/mJFDVRGXHsH5IgmloNPKfk26K6M= +git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250606194235-b5ccaea57560/go.mod h1:wKHPy1mpOCr9ahkRltwm1yi9qH/3m9xb8hMCX5C0L1o= 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-africastalking v0.0.0-20250129070628-5a539172c694 h1:DjJlBSz0S13acft5XZDWk7ZYnzElym0xLMYEVgyNJ+E= diff --git a/handlers/application/menuhandler.go b/handlers/application/menuhandler.go index 86f70b7..ebe4308 100644 --- a/handlers/application/menuhandler.go +++ b/handlers/application/menuhandler.go @@ -2245,6 +2245,110 @@ func (h *MenuHandlers) GetVoucherDetails(ctx context.Context, sym string, input 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 @@ -2716,25 +2820,27 @@ func (h *MenuHandlers) LoadSwapToList(ctx context.Context, sym string, input []b return res, nil } - defaultPool := dataserviceapi.PoolDetails{ - PoolName: config.DefaultPoolName(), - PoolSymbol: config.DefaultPoolSymbol(), - PoolContractAdrress: config.DefaultPoolAddress(), - LimiterAddress: "", - VoucherRegistry: "", - } - - activePoolAddress := defaultPool.PoolContractAdrress - - // set the active pool contract address - err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_POOL_ADDRESS, []byte(activePoolAddress)) + // Get active pool address or fall back to default + var activePoolAddress []byte + activePoolAddress, err = userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_POOL_ADDRESS) if err != nil { - logg.ErrorCtxf(ctx, "failed to write active PoolContractAdrress entry with", "key", storedb.DATA_ACTIVE_POOL_ADDRESS, "value", activePoolAddress, "error", err) - return res, err + if db.IsNotFound(err) { + defaultPoolAddress := config.DefaultPoolAddress() + // store the default as the active pool address + err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_POOL_ADDRESS, []byte(defaultPoolAddress)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write default PoolContractAdrress", "key", storedb.DATA_ACTIVE_POOL_ADDRESS, "value", defaultPoolAddress, "error", err) + return res, err + } + activePoolAddress = []byte(defaultPoolAddress) + } else { + logg.ErrorCtxf(ctx, "failed to read active PoolContractAdrress", "key", storedb.DATA_ACTIVE_POOL_ADDRESS, "error", err) + return res, err + } } // call the api using the ActivePoolAddress and ActiveVoucherAddress to check if it is part of the pool - r, err := h.accountService.CheckTokenInPool(ctx, activePoolAddress, string(activeAddress)) + r, err := h.accountService.CheckTokenInPool(ctx, string(activePoolAddress), string(activeAddress)) if err != nil { res.FlagSet = append(res.FlagSet, flag_api_error) logg.ErrorCtxf(ctx, "failed on CheckTokenInPool", "error", err) diff --git a/handlers/local.go b/handlers/local.go index 01c8800..4ff1e13 100644 --- a/handlers/local.go +++ b/handlers/local.go @@ -112,6 +112,10 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountService) ls.DbRs.AddLocalFunc("view_voucher", appHandlers.ViewVoucher) ls.DbRs.AddLocalFunc("set_voucher", appHandlers.SetVoucher) ls.DbRs.AddLocalFunc("get_voucher_details", appHandlers.GetVoucherDetails) + ls.DbRs.AddLocalFunc("get_default_pool", appHandlers.GetDefaultPool) + ls.DbRs.AddLocalFunc("get_pools", appHandlers.GetPools) + ls.DbRs.AddLocalFunc("view_pool", appHandlers.ViewPool) + ls.DbRs.AddLocalFunc("set_pool", appHandlers.SetPool) ls.DbRs.AddLocalFunc("validate_blocked_number", appHandlers.ValidateBlockedNumber) ls.DbRs.AddLocalFunc("retrieve_blocked_number", appHandlers.RetrieveBlockedNumber) ls.DbRs.AddLocalFunc("reset_unregistered_number", appHandlers.ResetUnregisteredNumber) diff --git a/services/registration/locale/swa/default.po b/services/registration/locale/swa/default.po index fbd92fb..235624a 100644 --- a/services/registration/locale/swa/default.po +++ b/services/registration/locale/swa/default.po @@ -38,4 +38,7 @@ msgid "%s balance: %s\n" msgstr "%s salio: %s\n" msgid "%s is not in %s. Please update your voucher and try again." -msgstr "%s haipo kwenye %s. Tafadhali badilisha sarafu yako na ujaribu tena." \ No newline at end of file +msgstr "%s haipo kwenye %s. Tafadhali badilisha sarafu yako na ujaribu tena." + +msgid "Name: %s\nSymbol: %s" +msgstr "Jina: %s\nSarafu: %s" \ No newline at end of file diff --git a/services/registration/my_vouchers.vis b/services/registration/my_vouchers.vis index e79438e..d196e07 100644 --- a/services/registration/my_vouchers.vis +++ b/services/registration/my_vouchers.vis @@ -2,8 +2,11 @@ LOAD reset_account_authorized 16 RELOAD reset_account_authorized MOUT select_voucher 1 MOUT voucher_details 2 +MOUT select_pool 3 MOUT back 0 HALT INCMP _ 0 INCMP select_voucher 1 INCMP voucher_details 2 +INCMP select_pool 3 +INCMP . * diff --git a/services/registration/pool_set b/services/registration/pool_set new file mode 100644 index 0000000..b5ec2f3 --- /dev/null +++ b/services/registration/pool_set @@ -0,0 +1 @@ +Success! {{.set_pool}} is now your active pool. \ No newline at end of file diff --git a/services/registration/pool_set.vis b/services/registration/pool_set.vis new file mode 100644 index 0000000..5c7b532 --- /dev/null +++ b/services/registration/pool_set.vis @@ -0,0 +1,10 @@ +LOAD reset_incorrect_pin 6 +CATCH _ flag_account_authorized 0 +LOAD set_pool 20 +MAP set_pool +MOUT back 0 +MOUT quit 9 +HALT +INCMP ^ 0 +INCMP quit 9 +INCMP ^ * diff --git a/services/registration/pool_set_swa b/services/registration/pool_set_swa new file mode 100644 index 0000000..3c527f6 --- /dev/null +++ b/services/registration/pool_set_swa @@ -0,0 +1 @@ +Hongera! {{.set_pool}} ni bwawa la Sarafu linalotumika sasa. \ No newline at end of file diff --git a/services/registration/select_pool b/services/registration/select_pool new file mode 100644 index 0000000..4daad6a --- /dev/null +++ b/services/registration/select_pool @@ -0,0 +1,3 @@ +Enter number or symbol to set the default pool: +Current: {{.get_default_pool}} +{{.get_pools}} \ No newline at end of file diff --git a/services/registration/select_pool.vis b/services/registration/select_pool.vis new file mode 100644 index 0000000..4436097 --- /dev/null +++ b/services/registration/select_pool.vis @@ -0,0 +1,20 @@ +CATCH no_voucher flag_no_active_voucher 1 +LOAD get_pools 0 +MAP get_pools +LOAD get_default_pool 20 +RELOAD get_default_pool +MAP get_default_pool +MOUT back 0 +MOUT quit 99 +MNEXT next 88 +MPREV prev 98 +HALT +INCMP > 88 +INCMP < 98 +INCMP _ 0 +INCMP quit 99 +LOAD view_pool 80 +RELOAD view_pool +CATCH api_failure flag_api_call_error 1 +CATCH . flag_incorrect_pool 1 +INCMP view_pool * diff --git a/services/registration/select_pool_menu b/services/registration/select_pool_menu new file mode 100644 index 0000000..5fc7cb4 --- /dev/null +++ b/services/registration/select_pool_menu @@ -0,0 +1 @@ +Select pool \ No newline at end of file diff --git a/services/registration/select_pool_menu_swa b/services/registration/select_pool_menu_swa new file mode 100644 index 0000000..546dc7d --- /dev/null +++ b/services/registration/select_pool_menu_swa @@ -0,0 +1 @@ +Chagua Bwawa \ No newline at end of file diff --git a/services/registration/select_pool_swa b/services/registration/select_pool_swa new file mode 100644 index 0000000..5efcc91 --- /dev/null +++ b/services/registration/select_pool_swa @@ -0,0 +1,3 @@ +Chagua nambari au ishara kuweka bwawa la sarafu: +La sasa: {{.get_default_pool}} +{{.get_pools}} \ No newline at end of file diff --git a/services/registration/view_pool b/services/registration/view_pool new file mode 100644 index 0000000..11109ea --- /dev/null +++ b/services/registration/view_pool @@ -0,0 +1,2 @@ +Enter PIN to confirm selection: +{{.view_pool}} \ No newline at end of file diff --git a/services/registration/view_pool.vis b/services/registration/view_pool.vis new file mode 100644 index 0000000..f506590 --- /dev/null +++ b/services/registration/view_pool.vis @@ -0,0 +1,10 @@ +MAP view_pool +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 pool_set * diff --git a/services/registration/view_pool_swa b/services/registration/view_pool_swa new file mode 100644 index 0000000..80c6a00 --- /dev/null +++ b/services/registration/view_pool_swa @@ -0,0 +1,2 @@ +Weka PIN ili kuthibitisha chaguo: +{{.view_pool}} \ No newline at end of file diff --git a/services/registration/voucher_set.vis b/services/registration/voucher_set.vis index 0773703..637bafd 100644 --- a/services/registration/voucher_set.vis +++ b/services/registration/voucher_set.vis @@ -1,5 +1,4 @@ LOAD reset_incorrect_pin 6 -CATCH incorrect_pin flag_incorrect_pin 1 CATCH _ flag_account_authorized 0 LOAD set_voucher 12 MAP set_voucher diff --git a/store/db/db.go b/store/db/db.go index e6e1eef..5ada4f6 100644 --- a/store/db/db.go +++ b/store/db/db.go @@ -85,6 +85,10 @@ const ( DATA_ACTIVE_SWAP_MAX_AMOUNT // Holds the active swap amount for the swap DATA_ACTIVE_SWAP_AMOUNT + // Holds the active pool name for the swap + DATA_ACTIVE_POOL_NAME + // Holds the active pool symbol for the swap + DATA_ACTIVE_POOL_SYM ) const ( diff --git a/store/pools.go b/store/pools.go index 1e01329..0cf918d 100644 --- a/store/pools.go +++ b/store/pools.go @@ -91,3 +91,52 @@ func MatchPool(input, names, symbols, addresses string) (name, symbol, address s } return } + +// StoreTemporaryPool saves pool metadata as temporary entries in the DataStore. +func StoreTemporaryPool(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.PoolDetails) error { + tempData := fmt.Sprintf("%s,%s,%s", data.PoolName, data.PoolSymbol, data.PoolContractAdrress) + + if err := store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(tempData)); err != nil { + return err + } + + return nil +} + +// GetTemporaryPoolData retrieves temporary pool metadata from the DataStore. +func GetTemporaryPoolData(ctx context.Context, store DataStore, sessionId string) (*dataserviceapi.PoolDetails, error) { + temp_data, err := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE) + if err != nil { + return nil, err + } + + values := strings.SplitN(string(temp_data), ",", 3) + + data := &dataserviceapi.PoolDetails{} + + data.PoolName = values[0] + data.PoolSymbol = values[1] + data.PoolContractAdrress = values[2] + + return data, nil +} + +// UpdatePoolData updates the active pool data in the DataStore. +func UpdatePoolData(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.PoolDetails) error { + logg.TraceCtxf(ctx, "dtal", "data", data) + // Active pool data entry + activeEntries := map[storedb.DataTyp][]byte{ + storedb.DATA_ACTIVE_POOL_NAME: []byte(data.PoolName), + storedb.DATA_ACTIVE_POOL_SYM: []byte(data.PoolSymbol), + storedb.DATA_ACTIVE_POOL_ADDRESS: []byte(data.PoolContractAdrress), + } + + // Write active data + for key, value := range activeEntries { + if err := store.WriteEntry(ctx, sessionId, key, value); err != nil { + return err + } + } + + return nil +} \ No newline at end of file