Compare commits

..

101 Commits

Author SHA1 Message Date
alfred-mk
21da4b976b use the updated sarafu-api that removes the publicKey from the GetPoolSwappableVouchers
Some checks failed
release / docker (push) Has been cancelled
2025-05-21 15:45:17 +03:00
alfred-mk
169d85e2cb added the INCLUDE_STABLES_PARAM env variable 2025-05-21 15:39:19 +03:00
alfred-mk
3ab76b3d22 use updated sarafu-api with corrected JSON mapping for checkTokenInPool and getSwapFromTokenMaxLimit
Some checks failed
release / docker (push) Has been cancelled
2025-05-20 15:53:12 +03:00
alfred-mk
b428e2e22d add log for the CheckTokenInPool
Some checks failed
release / docker (push) Has been cancelled
2025-05-20 14:02:41 +03:00
1d23a0cc4b Merge pull request 'default-env-pool' (#76) from default-env-pool into master
Some checks failed
release / docker (push) Has been cancelled
Reviewed-on: #76
2025-05-20 12:18:30 +02:00
alfred-mk
d3ad3b2db6 update the template and remove hardcoded values 2025-05-20 10:26:28 +03:00
alfred-mk
5d8fee470a use the default pool variables 2025-05-20 10:26:01 +03:00
alfred-mk
cf237c592a add the pool variables to the config 2025-05-20 10:25:27 +03:00
alfred-mk
2d8333b89a add the new env variables 2025-05-20 10:19:46 +03:00
alfred-mk
c8146ea211 alfred/pool-swap (#33)
Some checks failed
release / docker (push) Has been cancelled
Pool swap functionality

Resolves issue #24

Reviewed-on: #33
Co-authored-by: alfred-mk <alfredmwaik@gmail.com>
Co-committed-by: alfred-mk <alfredmwaik@gmail.com>
2025-05-19 15:44:50 +02:00
alfred-mk
d03c0c4c0e vouchers-input-selection (#75)
Some checks failed
release / docker (push) Has been cancelled
- Update the NEXT and PREV input selectors on the vouchers list
- Have the INCMPs before LOAD/RELOADs to prevent navigation inputs being passed to handler functions

Reviewed-on: #75
Reviewed-by: Mohamed Sohail <kamikazechaser@noreply.localhost>
Co-authored-by: alfred-mk <alfredmwaik@gmail.com>
Co-committed-by: alfred-mk <alfredmwaik@gmail.com>
2025-05-19 15:42:43 +02:00
alfred-mk
e914d059e2 upgraded vise to refresh error logline for more context
Some checks failed
release / docker (push) Has been cancelled
2025-05-07 20:26:54 +03:00
alfred-mk
7b1676bb37 fixes: recipient address and phone number (#74)
Some checks failed
release / docker (push) Has been cancelled
- Add functionality to checksum addresses...resolves #66
- Remove white spaces from the recipient input...resolves #70

Reviewed-on: #74
Reviewed-by: Mohamed Sohail <kamikazechaser@noreply.localhost>
Co-authored-by: alfred-mk <alfredmwaik@gmail.com>
Co-committed-by: alfred-mk <alfredmwaik@gmail.com>
2025-05-07 11:45:38 +02:00
carlos
db5d55d8e3 Merge pull request 'route-api-errors' (#46) from route-api-errors into master
Reviewed-on: #46
2025-04-29 13:55:54 +02:00
Carlosokumu
06cab56427 fix failing tests: menuhandlers missing the logdb 2025-04-29 13:25:31 +03:00
Carlosokumu
d0c181bca2 Merge branch 'master' into route-api-errors 2025-04-29 12:58:46 +03:00
e379129b3d Merge pull request 'logdb' (#56) from logdb into master
Some checks failed
release / docker (push) Has been cancelled
Reviewed-on: #56
2025-04-29 09:15:50 +02:00
Carlosokumu
29d35d1ec4 add write of the temporary and confirmed account pins to the log db 2025-04-28 15:49:40 +03:00
alfred-mk
687447e73e added the logdb to the testutil engine 2025-04-28 15:09:38 +03:00
alfred-mk
ec1ddefeb3 updated packages for sarafu-api and vise 2025-04-28 12:50:36 +03:00
alfred-mk
4166c9aed5 Merge branch 'master' into logdb 2025-04-28 12:38:27 +03:00
alfred-mk
78e82edfb5 added the logdn to the menuhandler tests 2025-04-28 12:26:06 +03:00
Carlosokumu
a5c4866b23 add required logdb to menuhandler 2025-04-28 11:50:25 +03:00
Carlosokumu
b52239e6be use direct key data type 2025-04-28 11:50:10 +03:00
Carlosokumu
5f02df64d7 upgrade visedriver dep 2025-04-28 11:40:28 +03:00
alfred-mk
0ba65fc48c use the updated phone and alias regexes from common 2025-04-28 11:39:37 +03:00
alfred-mk
ce8cc8e0fd use latest commit from sarafu-api 2025-04-28 11:38:45 +03:00
Carlosokumu
1499e4a29a check recipient before sending an sms 2025-04-28 11:38:44 +03:00
Carlosokumu
5f24ff3b1b feat: send invites when a recipient
is not resolved
2025-04-28 11:38:44 +03:00
Carlosokumu
6cdee7b58b update sarafu-api dep 2025-04-28 11:38:43 +03:00
alfred-mk
af5537abfc updated the description of DATA_SELF_PIN_RESET 2025-04-28 11:37:45 +03:00
alfred-mk
a6e569afb1 remove the verify_new_pin functionality that managed the unused flag_valid_pin 2025-04-28 11:37:44 +03:00
alfred-mk
446514d8ce remove unused function reset_valid_pin 2025-04-28 11:37:44 +03:00
alfred-mk
131d106f38 use a single confirm_pin_change node 2025-04-28 11:37:43 +03:00
alfred-mk
4d62f0222e use the same check for flag_invalid_pin to move to the invalid_pin node 2025-04-28 11:37:43 +03:00
alfred-mk
37cc3e1bc1 cleanup: remove extra space in all .vis files 2025-04-28 11:37:42 +03:00
alfred-mk
491e97d8af cleanup: remove extra space 2025-04-28 11:37:42 +03:00
alfred-mk
d4fefe3c39 update the order for the INCMP statement 2025-04-28 11:37:41 +03:00
alfred-mk
e8ff468c7b update the reset flags in TestCheckBlockedStatus 2025-04-28 11:37:41 +03:00
alfred-mk
965f343230 update the reset flags in TestConfirmPinChange 2025-04-28 11:37:40 +03:00
alfred-mk
3426d31a9e use the correct flag_invalid_pin in TestSaveTemporaryPin 2025-04-28 11:37:40 +03:00
alfred-mk
8b2b667307 updated the expected content for the alias in view profile 2025-04-28 11:37:39 +03:00
alfred-mk
c206fa1329 updated the menu flow for the admin reset others PIN in test 2025-04-28 11:37:39 +03:00
alfred-mk
ad395c4b1a remove code related to admins resetting a user's actual PIN 2025-04-28 11:37:38 +03:00
alfred-mk
af7e6cc603 add a space 2025-04-28 11:37:38 +03:00
alfred-mk
f90a188acc updated the flags set and reset in SaveTemporaryPin and VerifyCreatePin 2025-04-28 11:37:37 +03:00
alfred-mk
7a542d1f1c updated the description of the flag_incorrect_pin 2025-04-28 11:37:37 +03:00
alfred-mk
7d800d68a0 have the catch for create_pin above to catch accounts that do not have a PIN before the status on chain is checked 2025-04-28 11:37:36 +03:00
alfred-mk
850458b5d8 use a single pin_mismatch node for a confirmed PIN that does not match the original PIN 2025-04-28 11:37:36 +03:00
alfred-mk
ef8e3d5c35 allow the user to quit and move to invalid_pin node if the given input is invalid 2025-04-28 11:37:35 +03:00
alfred-mk
982eebf374 move to the top node when one selects back 2025-04-28 11:37:35 +03:00
alfred-mk
228d936a0e use the confirm_self_pin_reset to confirm the new PIN 2025-04-28 11:37:34 +03:00
alfred-mk
7c4d37b5b8 use the self_reset_pin node to reset PINs for accounts with a flag_account_pin_reset 2025-04-28 11:37:33 +03:00
alfred-mk
e6e8bb6671 set the DATA_SELF_PIN_RESET as 0 and reset the flag_account_pin_reset 2025-04-28 11:37:33 +03:00
alfred-mk
6385735b89 move back to the previous node if the admin user is not authorized 2025-04-28 11:34:27 +03:00
alfred-mk
70815aabd1 request the admin to authorize the PIN reset 2025-04-28 11:34:27 +03:00
alfred-mk
919899c704 move to the authorize_reset_others_pin if the given number is valid and registered 2025-04-28 11:34:26 +03:00
alfred-mk
9a2ad99d07 updated the ResetOthersPin and ValidateBlockedNumber for the updated reset other's PIN functionality 2025-04-28 11:34:26 +03:00
alfred-mk
2f4959e191 use the DATA_SELF_PIN_RESET to set the flag_account_pin_reset 2025-04-28 11:34:25 +03:00
alfred-mk
1b2f3bb046 added a DATA_SELF_PIN_RESET to track when a user's PIN is reset by an admin 2025-04-28 11:34:24 +03:00
carlos
19a2b28367 Merge pull request 'extra-sms' (#64) from extra-sms into master
Reviewed-on: #64
2025-04-28 10:33:25 +02:00
alfred-mk
25d124c58d use updated go-vise that removes rollback on missing key 2025-04-25 17:10:46 +03:00
alfred-mk
5b34ef28eb added the logDb to async and http 2025-04-25 09:33:08 +03:00
Carlosokumu
0370a3def4 use dedicated smsservice for triggering extra sms 2025-04-18 12:13:24 +03:00
Carlosokumu
7c0651d218 use dedicated sms package for triggering sms 2025-04-18 12:12:33 +03:00
Carlosokumu
a38ac06a3d trigger an address sms in the check_identifier handler 2025-04-18 10:56:41 +03:00
Carlosokumu
ac2a3721b2 remove dedicated address sms trigger 2025-04-18 10:55:02 +03:00
Carlosokumu
7ea3cb6b51 remove reload for check identifier to prevent double address sms trigger 2025-04-18 10:54:23 +03:00
Carlosokumu
0274133d48 add handler to send an address sms 2025-04-17 23:32:05 +03:00
Carlosokumu
5ff2e794ff feat: implement address and pin reset sms 2025-04-17 23:31:48 +03:00
Carlosokumu
08deeca500 register handler to trigger address sms 2025-04-17 23:31:22 +03:00
Carlosokumu
cea920c1f5 dep: upgrade sarafu-api dep to include address and pin reset sms 2025-04-17 23:30:54 +03:00
ade236747c Merge pull request 'use the updated phone and alias regexes from common' (#63) from update-recipient-validator into master
Reviewed-on: #63
2025-04-17 16:53:42 +02:00
alfred-mk
e1a3729e3b use the updated phone and alias regexes from common 2025-04-17 14:31:18 +03:00
Carlosokumu
f127fd7c0f initialize log db ,attach it to the menuhandler 2025-04-14 11:34:37 +03:00
Carlosokumu
79bf09f3d1 add logdb to menuhandler and associated write operations 2025-04-09 14:55:35 +03:00
Carlosokumu
f4804546d9 add log db struct with associated methods 2025-04-09 14:52:57 +03:00
Carlosokumu
3fff03a164 update visedriver dep 2025-04-09 14:50:28 +03:00
Carlosokumu
094866c129 add a logdb field to lhs and setter function 2025-04-09 14:49:41 +03:00
Carlosokumu
58242c8d55 fix failing test: TestManageVouchers 2025-04-02 10:53:36 +03:00
Carlosokumu
cab90ed89a set and reset flag_api_error flag 2025-04-02 10:48:04 +03:00
Carlosokumu
73c3486400 catch api call failure when manage vouchers is called 2025-04-02 10:47:33 +03:00
Carlosokumu
3c84fb5ae7 Merge branch 'master' into route-api-errors 2025-04-02 10:38:47 +03:00
Carlosokumu
04880b58a8 fix failing test 2025-04-01 12:49:10 +03:00
Carlosokumu
0458ac9498 fix failing test 2025-04-01 12:38:57 +03:00
Carlosokumu
a7e8c184f5 add some spacing 2025-04-01 11:59:31 +03:00
Carlosokumu
3615348efd add swahili template 2025-04-01 11:57:59 +03:00
Carlosokumu
d0be79d817 set account creation failed flag 2025-04-01 11:30:00 +03:00
Carlosokumu
7883063e53 feat: set and reset api call failure flags 2025-03-31 15:42:27 +03:00
Carlosokumu
f562ce8adf check for api call failure when setting the dafault voucher 2025-03-31 15:41:49 +03:00
Carlosokumu
cd2f4328a8 check for api call failure when checking transactions 2025-03-31 15:41:19 +03:00
Carlosokumu
95b9a6e486 catch api call failure when fetching voucher details 2025-03-28 16:09:53 +03:00
Carlosokumu
9674a04cbc add handler to reset api call failure flag,set flag when requesting an alias fails 2025-03-28 15:51:56 +03:00
Carlosokumu
6c46c097fb add explicit api call failure flag reset 2025-03-28 15:47:34 +03:00
Carlosokumu
c814c4ae5c register handler to reset api call failure flag 2025-03-28 15:46:57 +03:00
Carlosokumu
bbecec0310 catch api call error when requesting an alias 2025-03-28 15:46:21 +03:00
Carlosokumu
eaa89c29df Merge branch 'master' into route-api-errors 2025-03-28 15:22:40 +03:00
Carlosokumu
e832f46d22 return to top on retry 2025-03-26 14:40:54 +03:00
Carlosokumu
039117f40e add api call failure catch 2025-03-26 14:40:32 +03:00
Carlosokumu
3532f72fbd remove unneccessary catch 2025-03-26 14:39:37 +03:00
Carlosokumu
04c7e20457 set api call error flag to failing api calls 2025-03-26 14:38:47 +03:00
54 changed files with 1660 additions and 415 deletions

View File

@@ -21,3 +21,11 @@ LANGUAGES=eng, swa
#Alias search domains
ALIAS_SEARCH_DOMAINS=sarafu.local, sarafu.eth
#Pool swap
DEFAULT_POOL_NAME="Kenya ROLA Pool"
DEFAULT_POOL_SYMBOL=ROLA
DEFAULT_POOL_CONTRACT_ADDRESS=0x48a953cA5cf5298bc6f6Af3C608351f537AAcb9e
DEFAULT_LIMITER_ADDRESS=
DEFAULT_VOUCHER_REGISTRY=
INCLUDE_STABLES_PARAM=false

View File

@@ -44,6 +44,7 @@ func main() {
var err error
var gettextDir string
var langs args.LangVar
var logDbConnStr string
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
flag.StringVar(&override.DbConn, "c", "?", "default connection string (replaces all unspecified strings)")
@@ -55,6 +56,7 @@ func main() {
flag.UintVar(&port, "p", config.Port(), "http port")
flag.StringVar(&gettextDir, "gettext", "", "use gettext translations from given directory")
flag.Var(&langs, "language", "add symbol resolution for language")
flag.StringVar(&logDbConnStr, "log-c", "db-logs", "log db connection string")
flag.Parse()
config.Apply(override)
@@ -77,10 +79,10 @@ func main() {
pfp := path.Join(scriptDir, "pp.csv")
cfg := engine.Config{
Root: "root",
OutputSize: uint32(size),
FlagCount: uint32(128),
MenuSeparator: menuSeparator,
Root: "root",
OutputSize: uint32(size),
FlagCount: uint32(128),
MenuSeparator: menuSeparator,
ResetOnEmptyInput: true,
}
@@ -100,6 +102,11 @@ func main() {
fmt.Fprintf(os.Stderr, "userdatadb: %v\n", err)
os.Exit(1)
}
logdb, err := menuStorageService.GetLogDb(ctx, userdataStore, logDbConnStr, "user-data")
if err != nil {
fmt.Fprintf(os.Stderr, "get log db error: %v\n", err)
os.Exit(1)
}
dbResource, ok := rs.(*resource.DbResource)
if !ok {
@@ -113,6 +120,7 @@ func main() {
os.Exit(1)
}
lhs.SetDataStore(&userdataStore)
lhs.SetLogDb(&logdb)
if err != nil {
fmt.Fprintf(os.Stderr, "setdatastore: %v\n", err)
os.Exit(1)

View File

@@ -56,6 +56,7 @@ func main() {
var err error
var gettextDir string
var langs args.LangVar
var logDbConnStr string
flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
flag.StringVar(&override.DbConn, "c", "?", "default connection string (replaces all unspecified strings)")
@@ -69,6 +70,7 @@ func main() {
flag.UintVar(&port, "p", config.Port(), "http port")
flag.StringVar(&gettextDir, "gettext", "", "use gettext translations from given directory")
flag.Var(&langs, "language", "add symbol resolution for language")
flag.StringVar(&logDbConnStr, "log-c", "db-logs", "log db connection string")
flag.Parse()
config.Apply(override)
@@ -92,10 +94,10 @@ func main() {
pfp := path.Join(scriptDir, "pp.csv")
cfg := engine.Config{
Root: "root",
OutputSize: uint32(size),
FlagCount: uint32(128),
MenuSeparator: menuSeparator,
Root: "root",
OutputSize: uint32(size),
FlagCount: uint32(128),
MenuSeparator: menuSeparator,
ResetOnEmptyInput: true,
}
@@ -120,6 +122,12 @@ func main() {
fmt.Fprintf(os.Stderr, err.Error())
os.Exit(1)
}
logdb, err := menuStorageService.GetLogDb(ctx, userdataStore, logDbConnStr, "user-data")
if err != nil {
fmt.Fprintf(os.Stderr, "get log db error: %v\n", err)
os.Exit(1)
}
//defer userdataStore.Close(ctx)
dbResource, ok := rs.(*resource.DbResource)
@@ -129,6 +137,7 @@ func main() {
lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
lhs.SetDataStore(&userdataStore)
lhs.SetLogDb(&logdb)
accountService := services.New(ctx, menuStorageService)
hl, err := lhs.GetHandler(accountService)

View File

@@ -43,6 +43,7 @@ func main() {
var err error
var gettextDir string
var langs args.LangVar
var logDbConnStr string
flag.StringVar(&override.DbConn, "c", "?", "default connection string (replaces all unspecified strings)")
flag.StringVar(&override.UserConn, "userdata", "?", "userdata store connection string")
@@ -55,6 +56,7 @@ func main() {
flag.UintVar(&port, "p", config.Port(), "http port")
flag.StringVar(&gettextDir, "gettext", "", "use gettext translations from given directory")
flag.Var(&langs, "language", "add symbol resolution for language")
flag.StringVar(&logDbConnStr, "log-c", "db-logs", "log db connection string")
flag.Parse()
config.Apply(override)
@@ -78,10 +80,10 @@ func main() {
pfp := path.Join(scriptDir, "pp.csv")
cfg := engine.Config{
Root: "root",
OutputSize: uint32(size),
FlagCount: uint32(128),
MenuSeparator: menuSeparator,
Root: "root",
OutputSize: uint32(size),
FlagCount: uint32(128),
MenuSeparator: menuSeparator,
ResetOnEmptyInput: true,
}
@@ -103,6 +105,12 @@ func main() {
os.Exit(1)
}
logdb, err := menuStorageService.GetLogDb(ctx, userdataStore, logDbConnStr, "user-data")
if err != nil {
fmt.Fprintf(os.Stderr, "get log db error: %v\n", err)
os.Exit(1)
}
dbResource, ok := rs.(*resource.DbResource)
if !ok {
os.Exit(1)
@@ -110,6 +118,7 @@ func main() {
lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
lhs.SetDataStore(&userdataStore)
lhs.SetLogDb(&logdb)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())

View File

@@ -36,6 +36,7 @@ func main() {
var err error
var gettextDir string
var langs args.LangVar
var logDbConnStr string
flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
flag.StringVar(&override.DbConn, "c", "?", "default connection string (replaces all unspecified strings)")
@@ -46,6 +47,7 @@ func main() {
flag.UintVar(&size, "s", 160, "max size of output")
flag.StringVar(&gettextDir, "gettext", "", "use gettext translations from given directory")
flag.Var(&langs, "language", "add symbol resolution for language")
flag.StringVar(&logDbConnStr, "log-c", "db-logs", "log db connection string")
flag.Parse()
config.Apply(override)
@@ -110,6 +112,12 @@ func main() {
os.Exit(1)
}
logdb, err := menuStorageService.GetLogDb(ctx, userdatastore, logDbConnStr, "user-data")
if err != nil {
fmt.Fprintf(os.Stderr, "get log db error: %v\n", err)
os.Exit(1)
}
dbResource, ok := rs.(*resource.DbResource)
if !ok {
fmt.Fprintf(os.Stderr, "get dbresource error: %v\n", err)
@@ -118,6 +126,7 @@ func main() {
lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
lhs.SetDataStore(&userdatastore)
lhs.SetLogDb(&logdb)
lhs.SetPersister(pe)
if err != nil {
fmt.Fprintf(os.Stderr, "localhandler service error: %v\n", err)

View File

@@ -38,7 +38,7 @@ func main() {
var stateDebug bool
var host string
var port uint
flag.StringVar(&override.DbConn, "c", "?", "default connection string (replaces all unspecified strings)")
flag.StringVar(&override.ResourceConn, "resource", "?", "resource connection string")
flag.StringVar(&override.UserConn, "userdata", "?", "userdata store connection string")
@@ -81,9 +81,9 @@ func main() {
pfp := path.Join(scriptDir, "pp.csv")
cfg := engine.Config{
Root: "root",
OutputSize: uint32(size),
FlagCount: uint32(128),
Root: "root",
OutputSize: uint32(size),
FlagCount: uint32(128),
ResetOnEmptyInput: true,
}
if stateDebug {

View File

@@ -75,3 +75,15 @@ func PortSSH() uint {
func ATEndpoint() string {
return env.GetEnv("AT_ENDPOINT", "/")
}
func DefaultPoolAddress() string {
return env.GetEnv("DEFAULT_POOL_CONTRACT_ADDRESS", "")
}
func DefaultPoolName() string {
return env.GetEnv("DEFAULT_POOL_NAME", "")
}
func DefaultPoolSymbol() string {
return env.GetEnv("DEFAULT_POOL_SYMBOL", "")
}

View File

@@ -7,7 +7,6 @@ import (
"os"
"path"
"git.defalsify.org/vise.git/engine"
"git.defalsify.org/vise.git/logging"
"git.grassecon.net/grassrootseconomics/sarafu-vise/config"
@@ -17,9 +16,8 @@ import (
)
var (
logg = logging.NewVanilla().WithContextKey("SessionId")
scriptDir = path.Join("services", "registration")
menuSeparator = ": "
logg = logging.NewVanilla().WithContextKey("SessionId")
scriptDir = path.Join("services", "registration")
)
func main() {
@@ -27,8 +25,6 @@ func main() {
override := config.NewOverride()
var sessionId string
var size uint
var engineDebug bool
flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
flag.StringVar(&override.DbConn, "c", "?", "default connection string (replaces all unspecified strings)")
@@ -36,8 +32,6 @@ func main() {
flag.StringVar(&override.UserConn, "userdata", "?", "userdata store connection string")
flag.StringVar(&override.StateConn, "state", "?", "state store connection string")
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
flag.UintVar(&size, "s", 160, "max size of output")
flag.Parse()
config.Apply(override)
@@ -56,23 +50,13 @@ func main() {
os.Exit(1)
}
cfg := engine.Config{
Root: "root",
SessionId: sessionId,
OutputSize: uint32(size),
FlagCount: uint32(128),
MenuSeparator: menuSeparator,
EngineDebug: engineDebug,
ResetOnEmptyInput: true,
}
x := cmd.NewCmd(sessionId, flagParser)
x = x.WithEngine(cfg)
err = x.Parse(flag.Args())
if err != nil {
fmt.Fprintf(os.Stderr, "cmd parse fail: %v\n", err)
os.Exit(1)
}
logg.Infof("start command", "conn", conns, "subcmd", x)
menuStorageService := storage.NewMenuStorageService(conns)
@@ -86,4 +70,5 @@ func main() {
fmt.Fprintf(os.Stderr, "cmd exec error: %v\n", err)
os.Exit(1)
}
}

View File

@@ -24,7 +24,7 @@ func formatItem(k []byte, v []byte, sessionId string) (string, error) {
if err != nil {
return "", err
}
s := fmt.Sprintf("%v\n\t%v\n", o.Label, string(v))
s := fmt.Sprintf("%v\t%v\n", o.Label, string(v))
return s, nil
}

36
go.mod
View File

@@ -3,14 +3,14 @@ module git.grassecon.net/grassrootseconomics/sarafu-vise
go 1.23.4
require (
git.defalsify.org/vise.git v0.3.2-0.20250401123711-d481b04a6805
git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250411080608-34957e5b6ff8
git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2
git.defalsify.org/vise.git v0.3.2-0.20250507172020-cb22240f1cb9
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.20250521124206-343d30a2f27c
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
github.com/gofrs/uuid v4.4.0+incompatible
github.com/grassrootseconomics/ussd-data-service v1.2.0-beta
github.com/grassrootseconomics/ussd-data-service v1.4.4-beta
github.com/jackc/pgx/v5 v5.7.1
github.com/peteole/testdata-loader v0.3.0
github.com/stretchr/testify v1.9.0
@@ -19,24 +19,50 @@ require (
)
require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/StackExchange/wmi v1.2.1 // indirect
github.com/alecthomas/participle/v2 v2.0.0 // indirect
github.com/alecthomas/repr v0.2.0 // indirect
github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c // indirect
github.com/bits-and-blooms/bitset v1.14.3 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
github.com/consensys/bavard v0.1.13 // indirect
github.com/consensys/gnark-crypto v0.12.1 // indirect
github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c // indirect
github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/ethereum/c-kzg-4844 v1.0.0 // indirect
github.com/ethereum/go-ethereum v1.14.9 // indirect
github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grassrootseconomics/eth-custodial v1.3.0-beta // indirect
github.com/grassrootseconomics/ethutils v1.3.1 // indirect
github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 // indirect
github.com/hexops/gotextdiff v1.0.3 // indirect
github.com/holiman/uint256 v1.3.1 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/lmittmann/w3 v0.17.1 // indirect
github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a // indirect
github.com/mmcloughlin/addchain v0.4.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/supranational/blst v0.3.11 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/x448/float16 v0.8.4 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.7.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
rsc.io/tmplfunc v0.0.3 // indirect
)

102
go.sum
View File

@@ -1,23 +1,29 @@
git.defalsify.org/vise.git v0.3.1 h1:A6FhMcur09ft/JzUPGXR+KpA17fltfeBnasyvLMZmq4=
git.defalsify.org/vise.git v0.3.1/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
git.defalsify.org/vise.git v0.3.2-0.20250401123711-d481b04a6805 h1:FnT39aqXcP5YWhwPDBABopSjCu2SlbPFoOVitSpAVxU=
git.defalsify.org/vise.git v0.3.2-0.20250401123711-d481b04a6805/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d h1:5mzLas+jxTUtusOKx4XvU+n2QvrV/mH17MnJRy46siQ=
git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d/go.mod h1:wgQJZGIS6QuNLHqDhcsvehsbn5PvgV7aziRebMnJi60=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250310093912-8145b4bd004b h1:xiTpaqWWoF5qcnarY/9ZkT6aVdnKwqztb2gzIahJn4w=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250310093912-8145b4bd004b/go.mod h1:gOn89ipaDcDvmQXRMQYKUqcw/sJcwVOPVt2eC6Geip8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250401111804-2eed990921c5 h1:DwBZHP4sebfHxK8EU2nlA2CXU81+a7Kj/pnC5vDPcf4=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250401111804-2eed990921c5/go.mod h1:gOn89ipaDcDvmQXRMQYKUqcw/sJcwVOPVt2eC6Geip8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250401115503-5b41c8dc6440 h1:eWrBZMM3pBMCFyRl4rO/aaR+OmOMFJxogNyFAFry+EM=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250401115503-5b41c8dc6440/go.mod h1:gOn89ipaDcDvmQXRMQYKUqcw/sJcwVOPVt2eC6Geip8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250401122510-441e289854ad h1:tYjanaCf6mF+iXRtDx5gckQm5vhZYx9N/JlNIBZj1m0=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250401122510-441e289854ad/go.mod h1:gOn89ipaDcDvmQXRMQYKUqcw/sJcwVOPVt2eC6Geip8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250411080608-34957e5b6ff8 h1:Emesd0rybSLhPwZwqdvLsk/P9ZsT+7CQwQV/mrjQp3o=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250411080608-34957e5b6ff8/go.mod h1:gOn89ipaDcDvmQXRMQYKUqcw/sJcwVOPVt2eC6Geip8=
git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2 h1:YFztSsexCUgFo6M0tbngRwYdgJd3LQV3RO/Jw09u3+k=
git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2/go.mod h1:6B6ByxXOiRY0NR7K02Bf3fEu7z+2c/6q8PFVNjC5G8w=
git.defalsify.org/vise.git v0.3.2-0.20250425131748-8b84f59792ce h1:Uke2jQ4wG97gQKnTzxPyBGyhosrU1IWnRNFHtKVrmrk=
git.defalsify.org/vise.git v0.3.2-0.20250425131748-8b84f59792ce/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
git.defalsify.org/vise.git v0.3.2-0.20250507172020-cb22240f1cb9 h1:4kjbYw25MHZe9fqSbujPzpFXrYutFfVipvLrcWYnYks=
git.defalsify.org/vise.git v0.3.2-0.20250507172020-cb22240f1cb9/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
git.grassecon.net/grassrootseconomics/common v0.9.0-beta.1.0.20250417111317-2953f4c2f32e h1:DcC9qkNl9ny3hxQmsMK6W81+5R/j4ZwYUbvewMI/rlc=
git.grassecon.net/grassrootseconomics/common v0.9.0-beta.1.0.20250417111317-2953f4c2f32e/go.mod h1:wgQJZGIS6QuNLHqDhcsvehsbn5PvgV7aziRebMnJi60=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250428082711-5d221b8d565f h1:OAHCP3YR1C5h1WFnnEnLs5kn6jTxQHQYWYtQaMZJIMY=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250428082711-5d221b8d565f/go.mod h1:gOn89ipaDcDvmQXRMQYKUqcw/sJcwVOPVt2eC6Geip8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250516094326-3b85167ad84a h1:QNh0NaKtGbSeRPlTVKEAnqc0R5rnVrpDMrCHD/EaU5U=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250516094326-3b85167ad84a/go.mod h1:K/TPgZ4OhPHBQq2X0ab3JZs4YjiexzSURZcfHLs9Pf4=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250517113706-ee434dba6980 h1:FQUwTDFWduY7gCMi1U2OiFlsuAHLojWUw2hvZ4cGC2s=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250517113706-ee434dba6980/go.mod h1:K/TPgZ4OhPHBQq2X0ab3JZs4YjiexzSURZcfHLs9Pf4=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250517114512-050998ff82b1 h1:0hvILlGkZnXO8o7nZth4xu77vAS4zVQ6Ae0rb5x/Idg=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250517114512-050998ff82b1/go.mod h1:K/TPgZ4OhPHBQq2X0ab3JZs4YjiexzSURZcfHLs9Pf4=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250520125035-81fbc2574c30 h1:iZVmR/4xSoxahtkMBXs/z1HUSSnr+x1m+9AxXIgTvb0=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250520125035-81fbc2574c30/go.mod h1:R7+mt27mhm4/LCutAHHv87pOsLxXEPO93KXnhP8H6W0=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250521124206-343d30a2f27c h1:c2cA5hcYnLgs7kR4fF5tp42W6VQClTukbfKoaeskZu0=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250521124206-343d30a2f27c/go.mod h1:R7+mt27mhm4/LCutAHHv87pOsLxXEPO93KXnhP8H6W0=
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=
git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694/go.mod h1:DpibtYpnT3nG4Kn556hRAkdu4+CtiI/6MbnQHal51mQ=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk=
github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
github.com/alecthomas/participle/v2 v2.0.0 h1:Fgrq+MbuSsJwIkw3fEj9h75vDP0Er5JzepJ0/HNHv0g=
@@ -26,21 +32,58 @@ github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c h1:H9Nm+I7Cg/YVPpEV1RzU3Wq2pjamPc/UtHDgItcb7lE=
github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c/go.mod h1:rGod7o6KPeJ+hyBpHfhi4v7blx9sf+QsHsA7KAsdN6U=
github.com/bits-and-blooms/bitset v1.14.3 h1:Gd2c8lSNf9pKXom5JtD7AaKO8o7fGQ2LtFj1436qilA=
github.com/bits-and-blooms/bitset v1.14.3/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ=
github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M=
github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY=
github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I=
github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs=
github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI=
github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA=
github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
github.com/ethereum/go-ethereum v1.14.9 h1:J7iwXDrtUyE9FUjUYbd4c9tyzwMh6dTJsKzo9i6SrwA=
github.com/ethereum/go-ethereum v1.14.9/go.mod h1:QeW+MtTpRdBEm2pUFoonByee8zfHv7kGp0wK0odvU1I=
github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A=
github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grassrootseconomics/eth-custodial v1.3.0-beta h1:twrMBhl89GqDUL9PlkzQxMP/6OST1BByrNDj+rqXDmU=
github.com/grassrootseconomics/eth-custodial v1.3.0-beta/go.mod h1:7uhRcdnJplX4t6GKCEFkbeDhhjlcaGJeJqevbcvGLZo=
github.com/grassrootseconomics/ethutils v1.3.1 h1:LlQO90HjJkl7ejC8fv6jP7RJUrAm1j4VMMCYfsoIrhU=
github.com/grassrootseconomics/ethutils v1.3.1/go.mod h1:Wuv1VEZrkLIXqTSEYI3Nh9HG/ZHOUQ+U+xvWJ8QtjgQ=
github.com/grassrootseconomics/ussd-data-service v1.2.0-beta h1:fn1gwbWIwHVEBtUC2zi5OqTlfI/5gU1SMk0fgGixIXk=
github.com/grassrootseconomics/ussd-data-service v1.2.0-beta/go.mod h1:omfI0QtUwIdpu9gMcUqLMCG8O1XWjqJGBx1qUMiGWC0=
github.com/grassrootseconomics/ussd-data-service v1.4.0-beta h1:4fMd/3h2ZIhRg4GdHQmRw5FfD3MpJvFNNJQo+Q27f5M=
github.com/grassrootseconomics/ussd-data-service v1.4.0-beta/go.mod h1:9sGnorpKaK76FmOGXoh/xv7x5siSFNYdXxQo9BKW4DI=
github.com/grassrootseconomics/ussd-data-service v1.4.4-beta h1:turlyo0i3OLj29mWpWNoB/3Qao8qEngT/5d1jDWTZE4=
github.com/grassrootseconomics/ussd-data-service v1.4.4-beta/go.mod h1:9sGnorpKaK76FmOGXoh/xv7x5siSFNYdXxQo9BKW4DI=
github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 h1:U4kkNYryi/qfbBF8gh7Vsbuz+cVmhf5kt6pE9bYYyLo=
github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4/go.mod h1:zpZDgZFzeq9s0MIeB1P50NIEWDFFHSFBohI/NbaTD/Y=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/holiman/uint256 v1.3.1 h1:JfTzmih28bittyHM8z360dCjIA9dbPIBlcTI6lmctQs=
github.com/holiman/uint256 v1.3.1/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
@@ -55,8 +98,13 @@ github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lmittmann/w3 v0.17.1 h1:zdXIimmNmYfqOFur+Jqc9Yqwtq6jwnsQufbTOnSAtW4=
github.com/lmittmann/w3 v0.17.1/go.mod h1:WVUGMbL83WYBu4Sge3SVlW3qIG4VaHe+S8+UUnwz9Eg=
github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a h1:0Q3H0YXzMHiciXtRcM+j0jiCe8WKPQHoRgQiRTnfcLY=
github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a/go.mod h1:CdTTBOYzS5E4mWS1N8NWP6AHI19MP0A2B18n3hLzRMk=
github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU=
github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
github.com/pashagolub/pgxmock/v4 v4.3.0 h1:DqT7fk0OCK6H0GvqtcMsLpv8cIwWqdxWgfZNLeHCb/s=
github.com/pashagolub/pgxmock/v4 v4.3.0/go.mod h1:9VoVHXwS3XR/yPtKGzwQvwZX1kzGB9sM8SviDcHDa3A=
github.com/peteole/testdata-loader v0.3.0 h1:8jckE9KcyNHgyv/VPoaljvKZE0Rqr8+dPVYH6rfNr9I=
@@ -66,6 +114,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
@@ -73,18 +123,32 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4=
github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -93,3 +157,5 @@ gopkg.in/leonelquinteros/gotext.v1 v1.3.1/go.mod h1:X1WlGDeAFIYsW6GjgMm4VwUwZ2Xj
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=

View File

@@ -29,9 +29,11 @@ import (
"git.grassecon.net/grassrootseconomics/sarafu-api/models"
"git.grassecon.net/grassrootseconomics/sarafu-api/remote"
"git.grassecon.net/grassrootseconomics/sarafu-vise/config"
"git.grassecon.net/grassrootseconomics/sarafu-vise/internal/sms"
"git.grassecon.net/grassrootseconomics/sarafu-vise/profile"
"git.grassecon.net/grassrootseconomics/sarafu-vise/store"
storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db"
"github.com/grassrootseconomics/ethutils"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
)
@@ -77,18 +79,28 @@ type MenuHandlers struct {
flagManager *FlagManager
accountService remote.AccountService
prefixDb storedb.PrefixDb
smsService sms.SmsService
logDb store.LogDb
profile *profile.Profile
ReplaceSeparatorFunc func(string) string
}
// NewHandlers creates a new instance of the Handlers struct with the provided dependencies.
func NewMenuHandlers(appFlags *FlagManager, userdataStore db.Db, accountService remote.AccountService, replaceSeparatorFunc func(string) string) (*MenuHandlers, error) {
func NewMenuHandlers(appFlags *FlagManager, userdataStore db.Db, logdb db.Db, accountService remote.AccountService, replaceSeparatorFunc func(string) string) (*MenuHandlers, error) {
if userdataStore == nil {
return nil, fmt.Errorf("cannot create handler with nil userdata store")
}
userDb := &store.UserDataStore{
Db: userdataStore,
}
smsservice := sms.SmsService{
Accountservice: accountService,
Userdatastore: *userDb,
}
logDb := store.LogDb{
Db: logdb,
}
// Instantiate the SubPrefixDb with "DATATYPE_USERDATA" prefix
prefix := storedb.ToBytes(db.DATATYPE_USERDATA)
@@ -98,7 +110,9 @@ func NewMenuHandlers(appFlags *FlagManager, userdataStore db.Db, accountService
userdataStore: userDb,
flagManager: appFlags,
accountService: accountService,
smsService: smsservice,
prefixDb: prefixDb,
logDb: logDb,
profile: &profile.Profile{Max: 6},
ReplaceSeparatorFunc: replaceSeparatorFunc,
}
@@ -186,10 +200,16 @@ func (h *MenuHandlers) SetLanguage(ctx context.Context, sym string, input []byte
// handles the account creation when no existing account is present for the session and stores associated data in the user data store.
func (h *MenuHandlers) createAccountNoExist(ctx context.Context, sessionId string, res *resource.Result) error {
flag_account_created, _ := h.flagManager.GetFlag("flag_account_created")
flag_account_creation_failed, _ := h.flagManager.GetFlag("flag_account_creation_failed")
r, err := h.accountService.CreateAccount(ctx)
if err != nil {
return err
res.FlagSet = append(res.FlagSet, flag_account_creation_failed)
logg.ErrorCtxf(ctx, "failed to create an account", "error", err)
return nil
}
res.FlagReset = append(res.FlagReset, flag_account_creation_failed)
trackingId := r.TrackingId
publicKey := r.PublicKey
@@ -199,11 +219,16 @@ func (h *MenuHandlers) createAccountNoExist(ctx context.Context, sessionId strin
storedb.DATA_ACCOUNT_ALIAS: "",
}
store := h.userdataStore
logdb := h.logDb
for key, value := range data {
err = store.WriteEntry(ctx, sessionId, key, []byte(value))
if err != nil {
return err
}
err = logdb.WriteLogEntry(ctx, sessionId, key, []byte(value))
if err != nil {
logg.DebugCtxf(ctx, "Failed to write log entry", "key", key, "value", value)
}
}
publicKeyNormalized, err := hex.NormalizeHex(publicKey)
if err != nil {
@@ -213,6 +238,12 @@ func (h *MenuHandlers) createAccountNoExist(ctx context.Context, sessionId strin
if err != nil {
return err
}
err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId))
if err != nil {
logg.DebugCtxf(ctx, "Failed to write log entry", "key", storedb.DATA_PUBLIC_KEY_REVERSE, "value", sessionId)
}
res.FlagSet = append(res.FlagSet, flag_account_created)
return nil
}
@@ -382,12 +413,19 @@ func (h *MenuHandlers) SaveTemporaryPin(ctx context.Context, sym string, input [
}
store := h.userdataStore
logdb := h.logDb
err = store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(hashedPIN))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write temporaryAccountPIN entry with", "key", storedb.DATA_TEMPORARY_VALUE, "value", accountPIN, "error", err)
return res, err
}
err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(hashedPIN))
if err != nil {
logg.DebugCtxf(ctx, "Failed to write temporaryAccountPIN log entry", "key", storedb.DATA_TEMPORARY_VALUE, "value", accountPIN, "error", err)
}
return res, nil
}
@@ -399,6 +437,14 @@ func (h *MenuHandlers) ResetInvalidPIN(ctx context.Context, sym string, input []
return res, nil
}
// ResetApiCallFailure resets the api call failure flag
func (h *MenuHandlers) ResetApiCallFailure(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
res.FlagReset = append(res.FlagReset, flag_api_error)
return res, nil
}
// ConfirmPinChange validates user's new PIN. If input matches the temporary PIN, saves it as the new account PIN.
func (h *MenuHandlers) ConfirmPinChange(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
@@ -415,6 +461,7 @@ func (h *MenuHandlers) ConfirmPinChange(ctx context.Context, sym string, input [
}
store := h.userdataStore
logdb := h.logDb
hashedTemporaryPin, err := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read hashedTemporaryPin entry with", "key", storedb.DATA_TEMPORARY_VALUE, "error", err)
@@ -438,6 +485,12 @@ func (h *MenuHandlers) ConfirmPinChange(ctx context.Context, sym string, input [
logg.ErrorCtxf(ctx, "failed to write DATA_ACCOUNT_PIN entry with", "key", storedb.DATA_ACCOUNT_PIN, "hashedPIN value", hashedTemporaryPin, "error", err)
return res, err
}
err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_ACCOUNT_PIN, []byte(hashedTemporaryPin))
if err != nil {
logg.DebugCtxf(ctx, "Failed to write AccountPIN log entry", "key", storedb.DATA_ACCOUNT_PIN, "value", hashedTemporaryPin, "error", err)
}
// set the DATA_SELF_PIN_RESET as 0
err = store.WriteEntry(ctx, sessionId, storedb.DATA_SELF_PIN_RESET, []byte("0"))
if err != nil {
@@ -458,6 +511,7 @@ func (h *MenuHandlers) ValidateBlockedNumber(ctx context.Context, sym string, in
flag_unregistered_number, _ := h.flagManager.GetFlag("flag_unregistered_number")
store := h.userdataStore
logdb := h.logDb
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
@@ -493,6 +547,11 @@ func (h *MenuHandlers) ValidateBlockedNumber(ctx context.Context, sym string, in
return res, nil
}
err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_BLOCKED_NUMBER, []byte(formattedNumber))
if err != nil {
logg.DebugCtxf(ctx, "Failed to write blocked number log entry", "key", storedb.DATA_BLOCKED_NUMBER, "value", formattedNumber, "error", err)
}
return res, nil
}
@@ -502,7 +561,10 @@ func (h *MenuHandlers) ValidateBlockedNumber(ctx context.Context, sym string, in
// 3. Resetting the DATA_INCORRECT_PIN_ATTEMPTS to 0 for the blocked phone number
func (h *MenuHandlers) ResetOthersPin(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")
@@ -524,7 +586,15 @@ func (h *MenuHandlers) ResetOthersPin(ctx context.Context, sym string, input []b
logg.ErrorCtxf(ctx, "failed to reset incorrect PIN attempts", "key", storedb.DATA_INCORRECT_PIN_ATTEMPTS, "error", err)
return res, err
}
blockedPhoneStr := string(blockedPhonenumber)
//Trigger an SMS to inform a user that the blocked account has been reset
if phone.IsValidPhoneNumber(blockedPhoneStr) {
err = smsservice.SendPINResetSMS(ctx, sessionId, blockedPhoneStr)
if err != nil {
logg.DebugCtxf(ctx, "Failed to send PIN reset SMS", "error", err)
return res, nil
}
}
return res, nil
}
@@ -608,6 +678,8 @@ func (h *MenuHandlers) VerifyCreatePin(ctx context.Context, sym string, input []
}
store := h.userdataStore
logdb := h.logDb
hashedTemporaryPin, err := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read hashedTemporaryPin entry with", "key", storedb.DATA_TEMPORARY_VALUE, "error", err)
@@ -634,6 +706,11 @@ func (h *MenuHandlers) VerifyCreatePin(ctx context.Context, sym string, input []
return res, err
}
err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_ACCOUNT_PIN, []byte(hashedTemporaryPin))
if err != nil {
logg.DebugCtxf(ctx, "Failed to write DATA_ACCOUNT_PIN log entry", "key", storedb.DATA_ACCOUNT_PIN, "value", hashedTemporaryPin, "error", err)
}
return res, nil
}
@@ -646,7 +723,10 @@ func (h *MenuHandlers) SaveFirstname(ctx context.Context, sym string, input []by
return res, fmt.Errorf("missing session")
}
firstName := string(input)
store := h.userdataStore
logdb := h.logDb
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
flag_firstname_set, _ := h.flagManager.GetFlag("flag_firstname_set")
@@ -664,6 +744,11 @@ func (h *MenuHandlers) SaveFirstname(ctx context.Context, sym string, input []by
return res, err
}
res.FlagSet = append(res.FlagSet, flag_firstname_set)
err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_FIRST_NAME, []byte(temporaryFirstName))
if err != nil {
logg.DebugCtxf(ctx, "Failed to write firtname db log entry", "key", storedb.DATA_FIRST_NAME, "value", temporaryFirstName)
}
} else {
if firstNameSet {
err = store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(firstName))
@@ -689,6 +774,7 @@ func (h *MenuHandlers) SaveFamilyname(ctx context.Context, sym string, input []b
}
store := h.userdataStore
logdb := h.logDb
familyName := string(input)
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
@@ -708,6 +794,11 @@ func (h *MenuHandlers) SaveFamilyname(ctx context.Context, sym string, input []b
return res, err
}
res.FlagSet = append(res.FlagSet, flag_familyname_set)
err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_FAMILY_NAME, []byte(temporaryFamilyName))
if err != nil {
logg.DebugCtxf(ctx, "Failed to write firtname db log entry", "key", storedb.DATA_FAMILY_NAME, "value", temporaryFamilyName)
}
} else {
if familyNameSet {
err = store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(familyName))
@@ -764,6 +855,8 @@ func (h *MenuHandlers) SaveYob(ctx context.Context, sym string, input []byte) (r
}
yob := string(input)
store := h.userdataStore
logdb := h.logDb
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
flag_yob_set, _ := h.flagManager.GetFlag("flag_yob_set")
@@ -782,6 +875,11 @@ func (h *MenuHandlers) SaveYob(ctx context.Context, sym string, input []byte) (r
return res, err
}
res.FlagSet = append(res.FlagSet, flag_yob_set)
err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_YOB, []byte(temporaryYob))
if err != nil {
logg.DebugCtxf(ctx, "Failed to write yob db log entry", "key", storedb.DATA_YOB, "value", temporaryYob)
}
} else {
if yobSet {
err = store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(yob))
@@ -807,6 +905,7 @@ func (h *MenuHandlers) SaveLocation(ctx context.Context, sym string, input []byt
}
location := string(input)
store := h.userdataStore
logdb := h.logDb
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
flag_location_set, _ := h.flagManager.GetFlag("flag_location_set")
@@ -825,6 +924,11 @@ func (h *MenuHandlers) SaveLocation(ctx context.Context, sym string, input []byt
return res, err
}
res.FlagSet = append(res.FlagSet, flag_location_set)
err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_LOCATION, []byte(temporaryLocation))
if err != nil {
logg.DebugCtxf(ctx, "Failed to write location db log entry", "key", storedb.DATA_LOCATION, "value", temporaryLocation)
}
} else {
if locationSet {
err = store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(location))
@@ -852,6 +956,7 @@ func (h *MenuHandlers) SaveGender(ctx context.Context, sym string, input []byte)
}
gender := strings.Split(symbol, "_")[1]
store := h.userdataStore
logdb := h.logDb
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
flag_gender_set, _ := h.flagManager.GetFlag("flag_gender_set")
@@ -870,6 +975,12 @@ func (h *MenuHandlers) SaveGender(ctx context.Context, sym string, input []byte)
return res, err
}
res.FlagSet = append(res.FlagSet, flag_gender_set)
err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_GENDER, []byte(temporaryGender))
if err != nil {
logg.DebugCtxf(ctx, "Failed to write gender db log entry", "key", storedb.DATA_TEMPORARY_VALUE, "value", temporaryGender)
}
} else {
if genderSet {
err = store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(gender))
@@ -896,6 +1007,7 @@ func (h *MenuHandlers) SaveOfferings(ctx context.Context, sym string, input []by
offerings := string(input)
store := h.userdataStore
logdb := h.logDb
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
flag_offerings_set, _ := h.flagManager.GetFlag("flag_offerings_set")
@@ -915,6 +1027,11 @@ func (h *MenuHandlers) SaveOfferings(ctx context.Context, sym string, input []by
return res, err
}
res.FlagSet = append(res.FlagSet, flag_offerings_set)
err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_FIRST_NAME, []byte(temporaryOfferings))
if err != nil {
logg.DebugCtxf(ctx, "Failed to write offerings db log entry", "key", storedb.DATA_OFFERINGS, "value", offerings)
}
} else {
if offeringsSet {
err = store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(offerings))
@@ -1214,17 +1331,30 @@ func (h *MenuHandlers) ResetAccountAuthorized(ctx context.Context, sym string, i
return res, nil
}
// CheckIdentifier retrieves the PublicKey from the JSON data file.
// CheckIdentifier retrieves the Public key from the userdatastore under the key: DATA_PUBLIC_KEY and triggers an sms that
// will be sent to the associated session id
func (h *MenuHandlers) CheckIdentifier(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
smsservice := h.smsService
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
store := h.userdataStore
publicKey, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY)
publicKey, err := store.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
}
res.Content = string(publicKey)
//trigger an address sms to be delivered to the associated session id
err = smsservice.SendAddressSMS(ctx)
if err != nil {
logg.DebugCtxf(ctx, "Failed to trigger an address sms", "error", err)
return res, nil
}
return res, nil
}
@@ -1325,7 +1455,7 @@ func (h *MenuHandlers) CheckAccountStatus(ctx context.Context, sym string, input
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_error)
logg.ErrorCtxf(ctx, "failed on TrackAccountStatus", "error", err)
return res, err
return res, nil
}
res.FlagReset = append(res.FlagReset, flag_api_error)
@@ -1491,8 +1621,10 @@ func (h *MenuHandlers) ValidateRecipient(ctx context.Context, sym string, input
}
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")
recipient := string(input)
// remove white spaces
recipient := strings.ReplaceAll(string(input), " ", "")
if recipient != "0" {
recipientType, err := identity.CheckRecipient(recipient)
@@ -1542,8 +1674,11 @@ func (h *MenuHandlers) ValidateRecipient(ctx context.Context, sym string, input
}
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(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
@@ -1564,10 +1699,13 @@ func (h *MenuHandlers) ValidateRecipient(ctx context.Context, sym string, input
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
}
}
}
@@ -1621,6 +1759,7 @@ func (h *MenuHandlers) TransactionReset(ctx context.Context, sym string, input [
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 {
@@ -1642,7 +1781,7 @@ func (h *MenuHandlers) InviteValidRecipient(ctx context.Context, sym string, inp
return res, nil
}
_, err = h.accountService.SendUpsellSMS(ctx, sessionId, string(recipient))
_, 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
@@ -1880,6 +2019,7 @@ func (h *MenuHandlers) InitiateTransaction(ctx context.Context, sym string, inpu
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 {
@@ -1887,6 +2027,7 @@ func (h *MenuHandlers) ManageVouchers(ctx context.Context, sym string, input []b
}
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 {
@@ -1897,9 +2038,10 @@ func (h *MenuHandlers) ManageVouchers(ctx context.Context, sym string, input []b
// Fetch vouchers from API
vouchersResp, err := h.accountService.FetchVouchers(ctx, string(publicKey))
if err != nil {
res.FlagSet = append(res.FlagSet, flag_no_active_voucher)
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)
@@ -1934,6 +2076,10 @@ func (h *MenuHandlers) ManageVouchers(ctx context.Context, sym string, input []b
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)
@@ -2035,10 +2181,6 @@ func (h *MenuHandlers) ViewVoucher(ctx context.Context, sym string, input []byte
flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher")
inputStr := string(input)
if inputStr == "0" || inputStr == "99" {
res.FlagReset = append(res.FlagReset, flag_incorrect_voucher)
return res, nil
}
metadata, err := store.GetVoucherData(ctx, h.userdataStore, sessionId, inputStr)
if err != nil {
@@ -2110,6 +2252,7 @@ func (h *MenuHandlers) GetVoucherDetails(ctx context.Context, sym string, input
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,
@@ -2130,6 +2273,7 @@ func (h *MenuHandlers) CheckTransactions(ctx context.Context, sym string, input
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)
@@ -2143,6 +2287,7 @@ func (h *MenuHandlers) CheckTransactions(ctx context.Context, sym string, input
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 {
@@ -2169,6 +2314,10 @@ func (h *MenuHandlers) CheckTransactions(ctx context.Context, sym string, input
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)
@@ -2380,6 +2529,8 @@ func (h *MenuHandlers) RequestCustomAlias(ctx context.Context, sym string, input
return res, nil
}
flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
store := h.userdataStore
aliasHint, err := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE)
if err != nil {
@@ -2403,9 +2554,12 @@ func (h *MenuHandlers) RequestCustomAlias(ctx context.Context, sym string, input
sanitizedInput := sanitizeAliasHint(string(input))
aliasResult, err := h.accountService.RequestAlias(ctx, string(pubKey), sanitizedInput)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_error)
logg.ErrorCtxf(ctx, "failed to retrieve alias", "alias", string(aliasHint), "error_alias_request", err)
return res, fmt.Errorf("Failed to retrieve alias: %s", err.Error())
return res, nil
}
res.FlagReset = append(res.FlagReset, flag_api_error)
alias := aliasResult.Alias
logg.InfoCtxf(ctx, "Suggested alias ", "alias", alias)
@@ -2451,6 +2605,7 @@ func (h *MenuHandlers) GetSuggestedAlias(ctx context.Context, sym string, input
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")
@@ -2469,6 +2624,11 @@ func (h *MenuHandlers) ConfirmNewAlias(ctx context.Context, sym string, input []
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
}
@@ -2491,3 +2651,376 @@ func (h *MenuHandlers) ClearTemporaryValue(ctx context.Context, sym string, inpu
}
return res, nil
}
// GetPools fetches a list of 5 top pools
func (h *MenuHandlers) GetPools(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
flag_api_error, _ := h.flagManager.GetFlag("flag_api_error")
// call the api to get a list of top 5 pools sorted by swaps
topPools, err := h.accountService.FetchTopPools(ctx)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_error)
logg.ErrorCtxf(ctx, "failed on FetchTransactions", "error", err)
return res, err
}
// Return if there are no pools
if len(topPools) == 0 {
return res, nil
}
data := store.ProcessPools(topPools)
// Store all Pool data
dataMap := map[storedb.DataTyp]string{
storedb.DATA_POOL_NAMES: data.PoolNames,
storedb.DATA_POOL_SYMBOLS: data.PoolSymbols,
storedb.DATA_POOL_ADDRESSES: data.PoolContractAdrresses,
}
// 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
}
}
res.Content = h.ReplaceSeparatorFunc(data.PoolSymbols)
return res, nil
}
// LoadSwapFromList returns a list of possible vouchers to swap to
func (h *MenuHandlers) LoadSwapToList(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
// get the active address and symbol
activeAddress, err := userStore.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
}
activeSym, err := userStore.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
}
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher")
flag_api_error, _ := h.flagManager.GetFlag("flag_api_error")
inputStr := string(input)
if inputStr == "0" {
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))
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
}
// 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))
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_error)
logg.ErrorCtxf(ctx, "failed on CheckTokenInPool", "error", err)
return res, err
}
logg.InfoCtxf(ctx, "CheckTokenInPool", "response", r, "active_pool_address", activePoolAddress, "address", activeAddress)
if !r.CanSwapFrom {
res.FlagSet = append(res.FlagSet, flag_incorrect_voucher)
res.Content = l.Get(
"%s is not in %s. Please update your voucher and try again.",
activeSym,
config.DefaultPoolName(),
)
return res, nil
}
res.FlagReset = append(res.FlagReset, flag_incorrect_voucher)
// call the api using the activePoolAddress to get a list of SwapToSymbolsData
swapToList, err := h.accountService.GetPoolSwappableVouchers(ctx, string(activePoolAddress))
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_error)
logg.ErrorCtxf(ctx, "failed on FetchTransactions", "error", err)
return res, err
}
// Return if there are no vouchers
if len(swapToList) == 0 {
return res, nil
}
data := store.ProcessVouchers(swapToList)
// Store all to list voucher data
dataMap := map[storedb.DataTyp]string{
storedb.DATA_POOL_TO_SYMBOLS: data.Symbols,
storedb.DATA_POOL_TO_BALANCES: data.Balances,
storedb.DATA_POOL_TO_DECIMALS: data.Decimals,
storedb.DATA_POOL_TO_ADDRESSES: data.Addresses,
}
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
}
}
res.Content = h.ReplaceSeparatorFunc(data.Symbols)
return res, nil
}
// SwapMaxLimit returns the max FROM token
// check if max/tokenDecimals > 0.1 for UX purposes and to prevent swapping of dust values
func (h *MenuHandlers) SwapMaxLimit(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_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher")
flag_api_error, _ := h.flagManager.GetFlag("flag_api_error")
flag_low_swap_amount, _ := h.flagManager.GetFlag("flag_low_swap_amount")
res.FlagReset = append(res.FlagReset, flag_incorrect_voucher)
res.FlagReset = append(res.FlagSet, flag_low_swap_amount)
inputStr := string(input)
if inputStr == "0" {
return res, nil
}
userStore := h.userdataStore
metadata, err := store.GetSwapToVoucherData(ctx, userStore, sessionId, inputStr)
if err != nil {
return res, fmt.Errorf("failed to retrieve swap to voucher data: %v", err)
}
if metadata == nil {
res.FlagSet = append(res.FlagSet, flag_incorrect_voucher)
return res, nil
}
// Store the active swap_to data
if err := store.UpdateSwapToVoucherData(ctx, userStore, sessionId, metadata); err != nil {
logg.ErrorCtxf(ctx, "failed on UpdateSwapToVoucherData", "error", err)
return res, err
}
swapData, err := store.ReadSwapData(ctx, userStore, sessionId)
if err != nil {
return res, err
}
// call the api using the ActivePoolAddress, ActiveSwapFromAddress, ActiveSwapToAddress and PublicKey to get the swap max limit
r, err := h.accountService.GetSwapFromTokenMaxLimit(ctx, swapData.ActivePoolAddress, swapData.ActiveSwapFromAddress, swapData.ActiveSwapToAddress, swapData.PublicKey)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_error)
logg.ErrorCtxf(ctx, "failed on FetchTransactions", "error", err)
return res, err
}
// Scale down the amount
maxAmountStr := store.ScaleDownBalance(r.Max, swapData.ActiveSwapFromDecimal)
if err != nil {
return res, err
}
maxAmountFloat, err := strconv.ParseFloat(maxAmountStr, 64)
if err != nil {
logg.ErrorCtxf(ctx, "failed to parse maxAmountStr as float", "value", maxAmountStr, "error", err)
return res, err
}
// Format to 2 decimal places
maxStr := fmt.Sprintf("%.2f", maxAmountFloat)
if maxAmountFloat < 0.1 {
// return with low amount flag
res.Content = maxStr
res.FlagSet = append(res.FlagSet, flag_low_swap_amount)
return res, nil
}
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SWAP_MAX_AMOUNT, []byte(maxStr))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write swap max amount entry with", "key", storedb.DATA_ACTIVE_SWAP_MAX_AMOUNT, "value", maxStr, "error", err)
return res, err
}
res.Content = fmt.Sprintf(
"Maximum: %s\n\nEnter amount of %s to swap for %s:",
maxStr, swapData.ActiveSwapFromSym, swapData.ActiveSwapToSym,
)
return res, nil
}
// SwapPreview displays the swap preview and estimates
func (h *MenuHandlers) SwapPreview(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
inputStr := string(input)
if inputStr == "0" {
return res, nil
}
flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount")
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
userStore := h.userdataStore
swapData, err := store.ReadSwapPreviewData(ctx, userStore, sessionId)
if err != nil {
return res, err
}
maxValue, err := strconv.ParseFloat(swapData.ActiveSwapMaxAmount, 64)
if err != nil {
logg.ErrorCtxf(ctx, "Failed to convert the swapMaxAmount to a float", "error", err)
return res, err
}
inputAmount, err := strconv.ParseFloat(inputStr, 64)
if err != nil || inputAmount > maxValue {
res.FlagSet = append(res.FlagSet, flag_invalid_amount)
res.Content = inputStr
return res, nil
}
finalAmountStr, err := store.ParseAndScaleAmount(inputStr, swapData.ActiveSwapFromDecimal)
if err != nil {
return res, err
}
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SWAP_AMOUNT, []byte(finalAmountStr))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write swap amount entry with", "key", storedb.DATA_ACTIVE_SWAP_AMOUNT, "value", finalAmountStr, "error", err)
return res, err
}
// call the API to get the quote
r, err := h.accountService.GetPoolSwapQuote(ctx, finalAmountStr, swapData.PublicKey, swapData.ActiveSwapFromAddress, swapData.ActivePoolAddress, swapData.ActiveSwapToAddress)
if err != nil {
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 poolSwap", "error", err)
return res, nil
}
// Scale down the quoted amount
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
qouteStr := fmt.Sprintf("%.2f", qouteAmount)
res.Content = fmt.Sprintf(
"You will swap:\n%s %s for %s %s:",
inputStr, swapData.ActiveSwapFromSym, qouteStr, swapData.ActiveSwapToSym,
)
return res, nil
}
// InitiateSwap calls the poolSwap and returns a confirmation based on the result.
func (h *MenuHandlers) InitiateSwap(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
var err error
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized")
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
userStore := h.userdataStore
swapData, err := store.ReadSwapPreviewData(ctx, userStore, sessionId)
if err != nil {
return res, err
}
swapAmount, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SWAP_AMOUNT)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read swapAmount entry with", "key", storedb.DATA_ACTIVE_SWAP_AMOUNT, "error", err)
return res, err
}
swapAmountStr := string(swapAmount)
// Call the poolSwap API
r, err := h.accountService.PoolSwap(ctx, swapAmountStr, swapData.PublicKey, swapData.ActiveSwapFromAddress, swapData.ActivePoolAddress, swapData.ActiveSwapToAddress)
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 poolSwap", "error", err)
return res, nil
}
trackingId := r.TrackingId
logg.InfoCtxf(ctx, "poolSwap", "trackingId", trackingId)
res.Content = l.Get(
"Your request has been sent. You will receive an SMS when your %s %s has been swapped for %s.",
swapAmountStr,
swapData.ActiveSwapFromSym,
swapData.ActiveSwapToSym,
)
res.FlagReset = append(res.FlagReset, flag_account_authorized)
return res, nil
}

View File

@@ -62,6 +62,25 @@ func InitializeTestStore(t *testing.T) (context.Context, *store.UserDataStore) {
return ctx, store
}
// InitializeTestLogdbStore sets up and returns an in-memory database and logdb store.
func InitializeTestLogdbStore(t *testing.T) (context.Context, *store.UserDataStore) {
ctx := context.Background()
// Initialize memDb
db := memdb.NewMemDb()
err := db.Connect(ctx, "")
require.NoError(t, err, "Failed to connect to memDb")
// Create UserDataStore with memDb
logdb := &store.UserDataStore{Db: db}
t.Cleanup(func() {
db.Close(ctx) // Ensure the DB is closed after each test
})
return ctx, logdb
}
func InitializeTestSubPrefixDb(t *testing.T, ctx context.Context) *storedb.SubPrefixDb {
db := memdb.NewMemDb()
err := db.Connect(ctx, "")
@@ -76,6 +95,7 @@ func InitializeTestSubPrefixDb(t *testing.T, ctx context.Context) *storedb.SubPr
func TestNewMenuHandlers(t *testing.T) {
_, store := InitializeTestStore(t)
_, logdb := InitializeTestLogdbStore(t)
fm, err := NewFlagManager(flagsPath)
if err != nil {
@@ -86,7 +106,7 @@ func TestNewMenuHandlers(t *testing.T) {
// Test case for valid UserDataStore
t.Run("Valid UserDataStore", func(t *testing.T) {
handlers, err := NewMenuHandlers(fm, store, &accountService, mockReplaceSeparator)
handlers, err := NewMenuHandlers(fm, store, logdb, &accountService, mockReplaceSeparator)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
@@ -110,7 +130,7 @@ func TestNewMenuHandlers(t *testing.T) {
// Test case for nil UserDataStore
t.Run("Nil UserDataStore", func(t *testing.T) {
handlers, err := NewMenuHandlers(fm, nil, &accountService, mockReplaceSeparator)
handlers, err := NewMenuHandlers(fm, nil, logdb, &accountService, mockReplaceSeparator)
if err == nil {
t.Fatal("expected an error, got none")
}
@@ -192,8 +212,13 @@ func TestInit(t *testing.T) {
func TestCreateAccount(t *testing.T) {
sessionId := "session123"
ctx, store := InitializeTestStore(t)
ctx, userStore := InitializeTestStore(t)
ctx = context.WithValue(ctx, "SessionId", sessionId)
_, logdb := InitializeTestLogdbStore(t)
logDb := store.LogDb{
Db: logdb,
}
fm, err := NewFlagManager(flagsPath)
if err != nil {
@@ -201,6 +226,8 @@ func TestCreateAccount(t *testing.T) {
}
flag_account_created, err := fm.GetFlag("flag_account_created")
flag_account_creation_failed, _ := fm.GetFlag("flag_account_creation_failed")
if err != nil {
t.Logf(err.Error())
}
@@ -217,7 +244,8 @@ func TestCreateAccount(t *testing.T) {
PublicKey: "0xD3adB33f",
},
expectedResult: resource.Result{
FlagSet: []uint32{flag_account_created},
FlagSet: []uint32{flag_account_created},
FlagReset: []uint32{flag_account_creation_failed},
},
},
}
@@ -226,8 +254,9 @@ func TestCreateAccount(t *testing.T) {
mockAccountService := new(mocks.MockAccountService)
h := &MenuHandlers{
userdataStore: store,
userdataStore: userStore,
accountService: mockAccountService,
logDb: logDb,
flagManager: fm,
}
@@ -265,8 +294,13 @@ func TestWithPersister_PanicWhenAlreadySet(t *testing.T) {
func TestSaveFirstname(t *testing.T) {
sessionId := "session123"
ctx, store := InitializeTestStore(t)
ctx, userStore := InitializeTestStore(t)
ctx = context.WithValue(ctx, "SessionId", sessionId)
_, logdb := InitializeTestLogdbStore(t)
logDb := store.LogDb{
Db: logdb,
}
fm, _ := NewFlagManager(flagsPath)
@@ -282,7 +316,7 @@ func TestSaveFirstname(t *testing.T) {
// Define test data
firstName := "John"
if err := store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(firstName)); err != nil {
if err := userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(firstName)); err != nil {
t.Fatal(err)
}
@@ -290,9 +324,10 @@ func TestSaveFirstname(t *testing.T) {
// Create the MenuHandlers instance with the mock store
h := &MenuHandlers{
userdataStore: store,
userdataStore: userStore,
flagManager: fm,
st: mockState,
logDb: logDb,
}
// Call the method
@@ -303,14 +338,19 @@ func TestSaveFirstname(t *testing.T) {
assert.Equal(t, expectedResult, res)
// Verify that the DATA_FIRST_NAME entry has been updated with the temporary value
storedFirstName, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_FIRST_NAME)
storedFirstName, _ := userStore.ReadEntry(ctx, sessionId, storedb.DATA_FIRST_NAME)
assert.Equal(t, firstName, string(storedFirstName))
}
func TestSaveFamilyname(t *testing.T) {
sessionId := "session123"
ctx, store := InitializeTestStore(t)
ctx, userStore := InitializeTestStore(t)
ctx = context.WithValue(ctx, "SessionId", sessionId)
_, logdb := InitializeTestLogdbStore(t)
logDb := store.LogDb{
Db: logdb,
}
fm, _ := NewFlagManager(flagsPath)
@@ -328,15 +368,16 @@ func TestSaveFamilyname(t *testing.T) {
// Define test data
familyName := "Doeee"
if err := store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(familyName)); err != nil {
if err := userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(familyName)); err != nil {
t.Fatal(err)
}
// Create the MenuHandlers instance with the mock store
h := &MenuHandlers{
userdataStore: store,
userdataStore: userStore,
st: mockState,
flagManager: fm,
logDb: logDb,
}
// Call the method
@@ -347,14 +388,19 @@ func TestSaveFamilyname(t *testing.T) {
assert.Equal(t, expectedResult, res)
// Verify that the DATA_FAMILY_NAME entry has been updated with the temporary value
storedFamilyName, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_FAMILY_NAME)
storedFamilyName, _ := userStore.ReadEntry(ctx, sessionId, storedb.DATA_FAMILY_NAME)
assert.Equal(t, familyName, string(storedFamilyName))
}
func TestSaveYoB(t *testing.T) {
sessionId := "session123"
ctx, store := InitializeTestStore(t)
ctx, userStore := InitializeTestStore(t)
ctx = context.WithValue(ctx, "SessionId", sessionId)
_, logdb := InitializeTestLogdbStore(t)
logDb := store.LogDb{
Db: logdb,
}
fm, _ := NewFlagManager(flagsPath)
@@ -370,7 +416,7 @@ func TestSaveYoB(t *testing.T) {
// Define test data
yob := "1980"
if err := store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(yob)); err != nil {
if err := userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(yob)); err != nil {
t.Fatal(err)
}
@@ -378,9 +424,10 @@ func TestSaveYoB(t *testing.T) {
// Create the MenuHandlers instance with the mock store
h := &MenuHandlers{
userdataStore: store,
userdataStore: userStore,
flagManager: fm,
st: mockState,
logDb: logDb,
}
// Call the method
@@ -391,14 +438,19 @@ func TestSaveYoB(t *testing.T) {
assert.Equal(t, expectedResult, res)
// Verify that the DATA_YOB entry has been updated with the temporary value
storedYob, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_YOB)
storedYob, _ := userStore.ReadEntry(ctx, sessionId, storedb.DATA_YOB)
assert.Equal(t, yob, string(storedYob))
}
func TestSaveLocation(t *testing.T) {
sessionId := "session123"
ctx, store := InitializeTestStore(t)
ctx, userStore := InitializeTestStore(t)
ctx = context.WithValue(ctx, "SessionId", sessionId)
_, logdb := InitializeTestLogdbStore(t)
logDb := store.LogDb{
Db: logdb,
}
fm, _ := NewFlagManager(flagsPath)
@@ -414,7 +466,7 @@ func TestSaveLocation(t *testing.T) {
// Define test data
location := "Kilifi"
if err := store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(location)); err != nil {
if err := userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(location)); err != nil {
t.Fatal(err)
}
@@ -422,9 +474,10 @@ func TestSaveLocation(t *testing.T) {
// Create the MenuHandlers instance with the mock store
h := &MenuHandlers{
userdataStore: store,
userdataStore: userStore,
flagManager: fm,
st: mockState,
logDb: logDb,
}
// Call the method
@@ -435,14 +488,19 @@ func TestSaveLocation(t *testing.T) {
assert.Equal(t, expectedResult, res)
// Verify that the DATA_LOCATION entry has been updated with the temporary value
storedLocation, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_LOCATION)
storedLocation, _ := userStore.ReadEntry(ctx, sessionId, storedb.DATA_LOCATION)
assert.Equal(t, location, string(storedLocation))
}
func TestSaveOfferings(t *testing.T) {
sessionId := "session123"
ctx, store := InitializeTestStore(t)
ctx, userStore := InitializeTestStore(t)
ctx = context.WithValue(ctx, "SessionId", sessionId)
_, logdb := InitializeTestLogdbStore(t)
logDb := store.LogDb{
Db: logdb,
}
fm, _ := NewFlagManager(flagsPath)
@@ -458,7 +516,7 @@ func TestSaveOfferings(t *testing.T) {
// Define test data
offerings := "Bananas"
if err := store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(offerings)); err != nil {
if err := userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(offerings)); err != nil {
t.Fatal(err)
}
@@ -466,9 +524,10 @@ func TestSaveOfferings(t *testing.T) {
// Create the MenuHandlers instance with the mock store
h := &MenuHandlers{
userdataStore: store,
userdataStore: userStore,
flagManager: fm,
st: mockState,
logDb: logDb,
}
// Call the method
@@ -479,14 +538,19 @@ func TestSaveOfferings(t *testing.T) {
assert.Equal(t, expectedResult, res)
// Verify that the DATA_OFFERINGS entry has been updated with the temporary value
storedOfferings, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_OFFERINGS)
storedOfferings, _ := userStore.ReadEntry(ctx, sessionId, storedb.DATA_OFFERINGS)
assert.Equal(t, offerings, string(storedOfferings))
}
func TestSaveGender(t *testing.T) {
sessionId := "session123"
ctx, store := InitializeTestStore(t)
ctx, userStore := InitializeTestStore(t)
ctx = context.WithValue(ctx, "SessionId", sessionId)
_, logdb := InitializeTestLogdbStore(t)
logDb := store.LogDb{
Db: logdb,
}
fm, _ := NewFlagManager(flagsPath)
@@ -526,16 +590,17 @@ func TestSaveGender(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)); err != nil {
if err := userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)); err != nil {
t.Fatal(err)
}
mockState.ExecPath = append(mockState.ExecPath, tt.executingSymbol)
// Create the MenuHandlers instance with the mock store
h := &MenuHandlers{
userdataStore: store,
userdataStore: userStore,
st: mockState,
flagManager: fm,
logDb: logDb,
}
expectedResult := resource.Result{}
@@ -550,7 +615,7 @@ func TestSaveGender(t *testing.T) {
assert.Equal(t, expectedResult, res)
// Verify that the DATA_GENDER entry has been updated with the temporary value
storedGender, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_GENDER)
storedGender, _ := userStore.ReadEntry(ctx, sessionId, storedb.DATA_GENDER)
assert.Equal(t, tt.expectedGender, string(storedGender))
})
}
@@ -558,9 +623,15 @@ func TestSaveGender(t *testing.T) {
func TestSaveTemporaryPin(t *testing.T) {
sessionId := "session123"
ctx, store := InitializeTestStore(t)
ctx, userdatastore := InitializeTestStore(t)
ctx = context.WithValue(ctx, "SessionId", sessionId)
_, logdb := InitializeTestLogdbStore(t)
logDb := store.LogDb{
Db: logdb,
}
fm, err := NewFlagManager(flagsPath)
if err != nil {
log.Fatal(err)
@@ -571,7 +642,8 @@ func TestSaveTemporaryPin(t *testing.T) {
// Create the MenuHandlers instance with the mock flag manager
h := &MenuHandlers{
flagManager: fm,
userdataStore: store,
userdataStore: userdatastore,
logDb: logDb,
}
// Define test cases
@@ -612,9 +684,15 @@ func TestSaveTemporaryPin(t *testing.T) {
func TestCheckIdentifier(t *testing.T) {
sessionId := "session123"
ctx, store := InitializeTestStore(t)
ctx, userdatastore := InitializeTestStore(t)
ctx = context.WithValue(ctx, "SessionId", sessionId)
_, logdb := InitializeTestLogdbStore(t)
logDb := store.LogDb{
Db: logdb,
}
// Define test cases
tests := []struct {
name string
@@ -634,14 +712,15 @@ func TestCheckIdentifier(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := store.WriteEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY, []byte(tt.publicKey))
err := userdatastore.WriteEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY, []byte(tt.publicKey))
if err != nil {
t.Fatal(err)
}
// Create the MenuHandlers instance with the mock store
h := &MenuHandlers{
userdataStore: store,
userdataStore: userdatastore,
logDb: logDb,
}
// Call the method
@@ -1637,40 +1716,64 @@ func TestValidateRecipient(t *testing.T) {
// Define test cases
tests := []struct {
name string
input []byte
expectedResult resource.Result
name string
input []byte
expectError bool
expectedRecipient []byte
expectedResult resource.Result
}{
{
name: "Test with invalid recepient",
input: []byte("7?1234"),
name: "Test with invalid recepient",
input: []byte("7?1234"),
expectError: true,
expectedResult: resource.Result{
FlagSet: []uint32{flag_invalid_recipient},
Content: "7?1234",
},
},
{
name: "Test with valid unregistered recepient",
input: []byte("0712345678"),
name: "Test with valid unregistered recepient",
input: []byte("0712345678"),
expectError: true,
expectedResult: resource.Result{
FlagSet: []uint32{flag_invalid_recipient_with_invite},
Content: "0712345678",
},
},
{
name: "Test with valid registered recepient",
input: []byte("0711223344"),
expectedResult: resource.Result{},
name: "Test with valid registered recepient",
input: []byte("0711223344"),
expectError: false,
expectedRecipient: []byte(publicKey),
expectedResult: resource.Result{},
},
{
name: "Test with address",
input: []byte("0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9"),
expectedResult: resource.Result{},
name: "Test with address",
input: []byte("0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9"),
expectError: false,
expectedRecipient: []byte("0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9"),
expectedResult: resource.Result{},
},
{
name: "Test with alias recepient",
input: []byte("alias123.sarafu.local"),
expectedResult: resource.Result{},
name: "Test with alias recepient",
input: []byte("alias123.sarafu.local"),
expectError: false,
expectedRecipient: []byte("0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9"),
expectedResult: resource.Result{},
},
{
name: "Test for checksummed address",
input: []byte("0x5523058cdffe5f3c1eadadd5015e55c6e00fb439"),
expectError: false,
expectedRecipient: []byte("0x5523058cdFfe5F3c1EaDADD5015E55C6E00fb439"),
expectedResult: resource.Result{},
},
{
name: "Test with valid registered recepient that has white spaces",
input: []byte("0711 22 33 44"),
expectError: false,
expectedRecipient: []byte(publicKey),
expectedResult: resource.Result{},
},
}
@@ -1703,6 +1806,12 @@ func TestValidateRecipient(t *testing.T) {
t.Error(err)
}
if !tt.expectError {
storedRecipientAddress, err := store.ReadEntry(ctx, sessionId, storedb.DATA_RECIPIENT)
assert.NoError(t, err)
assert.Equal(t, tt.expectedRecipient, storedRecipientAddress)
}
// Assert that the Result FlagSet has the required flags after language switch
assert.Equal(t, res, tt.expectedResult, "Result should contain flag(s) that have been reset")
})
@@ -1950,8 +2059,13 @@ func TestManageVouchers(t *testing.T) {
sessionId := "session123"
publicKey := "0X13242618721"
ctx, store := InitializeTestStore(t)
ctx, userStore := InitializeTestStore(t)
ctx = context.WithValue(ctx, "SessionId", sessionId)
_, logdb := InitializeTestLogdbStore(t)
logDb := store.LogDb{
Db: logdb,
}
fm, err := NewFlagManager(flagsPath)
if err != nil {
@@ -1961,8 +2075,13 @@ func TestManageVouchers(t *testing.T) {
if err != nil {
t.Fatal(err)
}
flag_api_error, err := fm.GetFlag("flag_api_call_error")
err = store.WriteEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY, []byte(publicKey))
if err != nil {
t.Fatal(err)
}
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY, []byte(publicKey))
if err != nil {
t.Fatal(err)
}
@@ -1981,7 +2100,8 @@ func TestManageVouchers(t *testing.T) {
expectedVoucherSymbols: []byte(""),
expectedUpdatedAddress: []byte(""),
expectedResult: resource.Result{
FlagSet: []uint32{flag_no_active_voucher},
FlagSet: []uint32{flag_no_active_voucher},
FlagReset: []uint32{flag_api_error},
},
},
{
@@ -1997,7 +2117,7 @@ func TestManageVouchers(t *testing.T) {
expectedVoucherSymbols: []byte("1:TOKEN1"),
expectedUpdatedAddress: []byte("0x123"),
expectedResult: resource.Result{
FlagReset: []uint32{flag_no_active_voucher},
FlagReset: []uint32{flag_api_error, flag_no_active_voucher},
},
},
{
@@ -2010,7 +2130,7 @@ func TestManageVouchers(t *testing.T) {
expectedVoucherSymbols: []byte("1:SRF\n2:MILO"),
expectedUpdatedAddress: []byte("0xd4c288865Ce"),
expectedResult: resource.Result{
FlagReset: []uint32{flag_no_active_voucher},
FlagReset: []uint32{flag_api_error, flag_no_active_voucher},
},
},
}
@@ -2020,20 +2140,21 @@ func TestManageVouchers(t *testing.T) {
mockAccountService := new(mocks.MockAccountService)
h := &MenuHandlers{
userdataStore: store,
userdataStore: userStore,
accountService: mockAccountService,
flagManager: fm,
logDb: logDb,
}
mockAccountService.On("FetchVouchers", string(publicKey)).Return(tt.vouchersResp, nil)
// Store active voucher if needed
if tt.storedActiveVoucher != "" {
err := store.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM, []byte(tt.storedActiveVoucher))
err := userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM, []byte(tt.storedActiveVoucher))
if err != nil {
t.Fatal(err)
}
err = store.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_ADDRESS, []byte("0x41c188D45rfg6ds"))
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_ADDRESS, []byte("0x41c188D45rfg6ds"))
if err != nil {
t.Fatal(err)
}
@@ -2045,12 +2166,12 @@ func TestManageVouchers(t *testing.T) {
if tt.storedActiveVoucher != "" {
// Validate stored voucher symbols
voucherData, err := store.ReadEntry(ctx, sessionId, storedb.DATA_VOUCHER_SYMBOLS)
voucherData, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_VOUCHER_SYMBOLS)
assert.NoError(t, err)
assert.Equal(t, tt.expectedVoucherSymbols, voucherData)
// Validate stored active contract address
updatedAddress, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_ADDRESS)
updatedAddress, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_ADDRESS)
assert.NoError(t, err)
assert.Equal(t, tt.expectedUpdatedAddress, updatedAddress)
@@ -2161,6 +2282,8 @@ func TestGetVoucherDetails(t *testing.T) {
if err != nil {
t.Logf(err.Error())
}
flag_api_error, _ := fm.GetFlag("flag_api_call_error")
mockAccountService := new(mocks.MockAccountService)
sessionId := "session123"
@@ -2188,8 +2311,8 @@ func TestGetVoucherDetails(t *testing.T) {
"Name: %s\nSymbol: %s\nCommodity: %s\nLocation: %s", tokenDetails.TokenName, tokenDetails.TokenSymbol, tokenDetails.TokenCommodity, tokenDetails.TokenLocation,
)
mockAccountService.On("VoucherData", string(tokA_AAddress)).Return(tokenDetails, nil)
res, err := h.GetVoucherDetails(ctx, "SessionId", []byte(""))
expectedResult.FlagReset = append(expectedResult.FlagReset, flag_api_error)
assert.NoError(t, err)
assert.Equal(t, expectedResult, res)
}
@@ -2308,7 +2431,7 @@ func TestCheckBlockedStatus(t *testing.T) {
{
name: "Currently blocked account",
currentWrongPinAttempts: "4",
expectedResult: resource.Result{
expectedResult: resource.Result{
FlagReset: []uint32{flag_account_pin_reset},
},
},
@@ -2377,8 +2500,14 @@ func TestCheckTransactions(t *testing.T) {
sessionId := "session123"
publicKey := "0X13242618721"
ctx, store := InitializeTestStore(t)
ctx, userStore := InitializeTestStore(t)
ctx = context.WithValue(ctx, "SessionId", sessionId)
_, logdb := InitializeTestLogdbStore(t)
logDb := store.LogDb{
Db: logdb,
}
spdb := InitializeTestSubPrefixDb(t, ctx)
fm, err := NewFlagManager(flagsPath)
@@ -2387,13 +2516,14 @@ func TestCheckTransactions(t *testing.T) {
}
h := &MenuHandlers{
userdataStore: store,
userdataStore: userStore,
accountService: mockAccountService,
prefixDb: spdb,
logDb: logDb,
flagManager: fm,
}
err = store.WriteEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY, []byte(publicKey))
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY, []byte(publicKey))
if err != nil {
t.Fatal(err)
}

View File

@@ -27,6 +27,7 @@ type LocalHandlerService struct {
DbRs *resource.DbResource
Pe *persist.Persister
UserdataStore *db.Db
LogDb *db.Db
Cfg engine.Config
Rs resource.Resource
first resource.EntryFunc
@@ -57,12 +58,16 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) {
ls.UserdataStore = db
}
func (ls *LocalHandlerService) SetLogDb(db *db.Db) {
ls.LogDb = db
}
func (ls *LocalHandlerService) GetHandler(accountService remote.AccountService) (*application.MenuHandlers, error) {
replaceSeparatorFunc := func(input string) string {
return strings.ReplaceAll(input, ":", ls.Cfg.MenuSeparator)
}
appHandlers, err := application.NewMenuHandlers(ls.Parser, *ls.UserdataStore, accountService, replaceSeparatorFunc)
appHandlers, err := application.NewMenuHandlers(ls.Parser, *ls.UserdataStore, *ls.LogDb, accountService, replaceSeparatorFunc)
if err != nil {
return nil, err
}
@@ -124,7 +129,11 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountService)
ls.DbRs.AddLocalFunc("get_suggested_alias", appHandlers.GetSuggestedAlias)
ls.DbRs.AddLocalFunc("confirm_new_alias", appHandlers.ConfirmNewAlias)
ls.DbRs.AddLocalFunc("check_account_created", appHandlers.CheckAccountCreated)
ls.DbRs.AddLocalFunc("reset_api_call_failure", appHandlers.ResetApiCallFailure)
ls.DbRs.AddLocalFunc("swap_to_list", appHandlers.LoadSwapToList)
ls.DbRs.AddLocalFunc("swap_max_limit", appHandlers.SwapMaxLimit)
ls.DbRs.AddLocalFunc("swap_preview", appHandlers.SwapPreview)
ls.DbRs.AddLocalFunc("initiate_swap", appHandlers.InitiateSwap)
ls.first = appHandlers.Init
return appHandlers, nil

View File

@@ -3,45 +3,23 @@ package cmd
import (
"context"
"fmt"
"regexp"
"strings"
"git.defalsify.org/vise.git/db"
"git.defalsify.org/vise.git/engine"
"git.defalsify.org/vise.git/logging"
"git.defalsify.org/vise.git/persist"
"git.defalsify.org/vise.git/resource"
"git.defalsify.org/vise.git/state"
"git.grassecon.net/grassrootseconomics/sarafu-vise/handlers/application"
"git.grassecon.net/grassrootseconomics/visedriver/storage"
storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db"
)
var argc map[string]int = map[string]int{
"reset": 0,
"admin": 1,
"clone": 1,
"overwrite": 2,
}
var (
logg = logging.NewVanilla().WithDomain("cmd").WithContextKey("SessionId")
cloneTargetRegex = `^\+000`
logg = logging.NewVanilla().WithDomain("cmd").WithContextKey("SessionId")
)
type Cmd struct {
sessionId string
conn storage.ConnData
flagParser *application.FlagManager
cmd int
enable bool
param string
exec func(ctx context.Context, ss storage.StorageService) error
engineConfig *engine.Config
st *state.State
key string
value string
sessionId string
conn storage.ConnData
flagParser *application.FlagManager
cmd int
enable bool
exec func(ctx context.Context, ss storage.StorageService) error
}
func NewCmd(sessionId string, flagParser *application.FlagManager) *Cmd {
@@ -51,115 +29,10 @@ func NewCmd(sessionId string, flagParser *application.FlagManager) *Cmd {
}
}
func (c *Cmd) WithEngine(engineConfig engine.Config) *Cmd {
c.engineConfig = &engineConfig
return c
}
func (c *Cmd) Exec(ctx context.Context, ss storage.StorageService) error {
return c.exec(ctx, ss)
}
func (c *Cmd) engine(ctx context.Context, rs resource.Resource, pe *persist.Persister) (engine.Engine, error) {
if c.engineConfig == nil {
return nil, fmt.Errorf("engine config missing")
}
en := engine.NewEngine(*c.engineConfig, rs)
st := pe.GetState()
if st == nil {
return nil, fmt.Errorf("persister state fail")
}
en = en.WithState(st)
st.UseDebug()
ca := pe.GetMemory()
if ca == nil {
return nil, fmt.Errorf("persister cache fail")
}
en = en.WithMemory(ca)
logg.DebugCtxf(ctx, "state loaded", "state", st)
return en, nil
}
func (c *Cmd) execClone(ctx context.Context, ss storage.StorageService) error {
re := regexp.MustCompile(cloneTargetRegex)
if !re.MatchString(c.param) {
return fmt.Errorf("Clone sessionId must match target: %s", c.param)
}
pe, err := ss.GetPersister(ctx)
if err != nil {
return fmt.Errorf("get persister error: %v", err)
}
err = pe.Load(c.engineConfig.SessionId)
if err != nil {
return fmt.Errorf("persister load error: %v", err)
}
/// TODO consider DRY with devtools/store/dump
store, err := ss.GetUserdataDb(ctx)
if err != nil {
return fmt.Errorf("store retrieve error: %v", err)
}
store.SetSession(c.engineConfig.SessionId)
store.SetPrefix(db.DATATYPE_USERDATA)
dmp, err := store.Dump(ctx, []byte(""))
if err != nil {
return fmt.Errorf("store dump fail: %v\n", err.Error())
}
for true {
store.SetSession(c.engineConfig.SessionId)
k, v := dmp.Next(ctx)
if k == nil {
break
}
store.SetSession(c.param)
err = store.Put(ctx, k, v)
if err != nil {
return fmt.Errorf("user data store clone failed on key: %x", k)
}
}
return pe.Save(c.param)
}
func (c *Cmd) execReset(ctx context.Context, ss storage.StorageService) error {
pe, err := ss.GetPersister(ctx)
if err != nil {
return fmt.Errorf("get persister error: %v", err)
}
rs, err := ss.GetResource(ctx)
if err != nil {
return fmt.Errorf("get resource error: %v", err)
}
dbResource, ok := rs.(*resource.DbResource)
if !ok {
return fmt.Errorf("get dbresource error: %v", err)
}
err = pe.Load(c.engineConfig.SessionId)
if err != nil {
return fmt.Errorf("persister load error: %v", err)
}
en, err := c.engine(ctx, dbResource, pe)
if err != nil {
return err
}
_, err = en.(*engine.DefaultEngine).Reset(ctx, false)
if err != nil {
return err
}
st := pe.GetState()
logg.DebugCtxf(ctx, "state after reset", "state", st)
err = pe.Save(c.engineConfig.SessionId)
if err != nil {
return err
}
return nil
}
func (c *Cmd) execAdmin(ctx context.Context, ss storage.StorageService) error {
pe, err := ss.GetPersister(ctx)
if err != nil {
@@ -190,51 +63,6 @@ func (c *Cmd) execAdmin(ctx context.Context, ss storage.StorageService) error {
return nil
}
func (c *Cmd) execOverwrite(ctx context.Context, ss storage.StorageService) error {
store, err := ss.GetUserdataDb(ctx)
if err != nil {
return fmt.Errorf("failed to get userdata store: %v", err)
}
// Map of symbolic keys to their DataTyp constants
symbolicKeys := map[string]storedb.DataTyp{
"first_name": storedb.DATA_FIRST_NAME,
"family_name": storedb.DATA_FAMILY_NAME,
"yob": storedb.DATA_YOB,
"location": storedb.DATA_LOCATION,
"gender": storedb.DATA_GENDER,
"offerings": storedb.DATA_OFFERINGS,
}
// Lookup symbolic key
dtype, ok := symbolicKeys[strings.ToLower(c.key)]
if !ok {
return fmt.Errorf("unknown key '%s'. Available keys: %v", c.key, keysOf(symbolicKeys))
}
k := storedb.ToBytes(dtype)
store.SetPrefix(db.DATATYPE_USERDATA)
store.SetSession(c.sessionId)
err = store.Put(ctx, k, []byte(c.value))
if err != nil {
return fmt.Errorf("failed to overwrite entry for key %s: %v", c.key, err)
}
logg.InfoCtxf(ctx, "overwrote data", "sessionId", c.sessionId, "key", c.key, "value", c.value)
return nil
}
// keysOf returns a list of keys from the symbolic map for error messages
func keysOf(m map[string]storedb.DataTyp) []string {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
}
func (c *Cmd) parseCmdAdmin(cmd string, param string, more []string) (bool, error) {
if cmd == "admin" {
if param == "1" {
@@ -248,56 +76,13 @@ func (c *Cmd) parseCmdAdmin(cmd string, param string, more []string) (bool, erro
return false, nil
}
func (c *Cmd) parseCmdReset(cmd string, param string, more []string) (bool, error) {
if cmd == "reset" {
c.enable = false
c.exec = c.execReset
return true, nil
}
return false, nil
}
func (c *Cmd) parseCmdClone(cmd string, param string, more []string) (bool, error) {
if cmd == "clone" {
c.enable = false
c.param = param
c.exec = c.execClone
return true, nil
}
return false, nil
}
func (c *Cmd) parseCmdOverwrite(cmd string, param string, more []string) (bool, error) {
if cmd == "overwrite" {
if len(more) < 1 {
return false, fmt.Errorf("overwrite requires key and value")
}
c.key = param
c.value = more[0]
c.exec = c.execOverwrite
return true, nil
}
return false, nil
}
func (c *Cmd) Parse(args []string) error {
var param string
if len(args) < 1 {
if len(args) < 2 {
return fmt.Errorf("Wrong number of arguments: %v", args)
}
cmd := args[0]
n, ok := argc[cmd]
if !ok {
return fmt.Errorf("invalid command: %v", cmd)
}
if n > 0 {
if len(args) < n+1 {
return fmt.Errorf("Wrong number of arguments, need: %d", n)
}
param = args[1]
args = args[2:]
}
param := args[1]
args = args[2:]
r, err := c.parseCmdAdmin(cmd, param, args)
if err != nil {
@@ -307,29 +92,5 @@ func (c *Cmd) Parse(args []string) error {
return nil
}
r, err = c.parseCmdReset(cmd, param, args)
if err != nil {
return err
}
if r {
return nil
}
r, err = c.parseCmdClone(cmd, param, args)
if err != nil {
return err
}
if r {
return nil
}
r, err = c.parseCmdOverwrite(cmd, param, args)
if err != nil {
return err
}
if r {
return nil
}
return fmt.Errorf("unknown subcommand: %s", cmd)
}

96
internal/sms/sms.go Normal file
View File

@@ -0,0 +1,96 @@
package sms
import (
"context"
"fmt"
"git.defalsify.org/vise.git/logging"
"git.grassecon.net/grassrootseconomics/common/phone"
"git.grassecon.net/grassrootseconomics/sarafu-api/remote"
"git.grassecon.net/grassrootseconomics/sarafu-vise/store"
storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db"
)
var (
logg = logging.NewVanilla().WithDomain("smsservice")
)
type SmsService struct {
Accountservice remote.AccountService
Userdatastore store.UserDataStore
}
// SendUpsellSMS will send an invitation SMS to an unregistered phone number
func (smsservice *SmsService) SendUpsellSMS(ctx context.Context, inviterPhone, inviteePhone string) error {
if !phone.IsValidPhoneNumber(inviterPhone) {
return fmt.Errorf("invalid inviter phone number %v", inviterPhone)
}
if !phone.IsValidPhoneNumber(inviteePhone) {
return fmt.Errorf("Invalid invitee phone number %v", inviteePhone)
}
_, err := smsservice.Accountservice.SendUpsellSMS(ctx, inviterPhone, inviteePhone)
if err != nil {
return fmt.Errorf("Failed to send upsell sms: %v", err)
}
return nil
}
// sendPINResetSMS will send an SMS to a user's phonenumber in the event that the associated account's PIN has been reset.
func (smsService *SmsService) SendPINResetSMS(ctx context.Context, adminPhoneNumber, blockedPhoneNumber string) error {
formattedAdminPhone, err := phone.FormatPhoneNumber(adminPhoneNumber)
if err != nil {
return fmt.Errorf("failed to format admin phone number: %w", err)
}
formattedBlockedPhone, err := phone.FormatPhoneNumber(blockedPhoneNumber)
if err != nil {
return fmt.Errorf("failed to format blocked phone number: %w", err)
}
if !phone.IsValidPhoneNumber(formattedAdminPhone) {
return fmt.Errorf("invalid admin phone number")
}
if !phone.IsValidPhoneNumber(formattedBlockedPhone) {
return fmt.Errorf("invalid blocked phone number")
}
err = smsService.Accountservice.SendPINResetSMS(ctx, formattedAdminPhone, formattedBlockedPhone)
if err != nil {
return fmt.Errorf("failed to send pin reset sms: %v", err)
}
return nil
}
// SendAddressSMS will triger an SMS when a user navigates to the my address node.The SMS will be sent to the associated phonenumber.
func (smsService *SmsService) SendAddressSMS(ctx context.Context) error {
store := smsService.Userdatastore
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return fmt.Errorf("missing session")
}
publicKey, err := store.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 err
}
originPhone, err := phone.FormatPhoneNumber(sessionId)
if err != nil {
logg.DebugCtxf(ctx, "Failed to format origin phonenumber", "sessionid", sessionId)
return nil
}
if !phone.IsValidPhoneNumber(originPhone) {
logg.InfoCtxf(ctx, "Invalid origin phone number", "origin phonenumber", originPhone)
return fmt.Errorf("invalid origin phone number")
}
err = smsService.Accountservice.SendAddressSMS(ctx, string(publicKey), originPhone)
if err != nil {
logg.DebugCtxf(ctx, "Failed to send address sms", "error", err)
return fmt.Errorf("Failed to send address sms: %v", err)
}
return nil
}

View File

@@ -10,9 +10,9 @@ import (
func TestInsertOrShift(t *testing.T) {
tests := []struct {
name string
profile Profile
index int
value string
profile Profile
index int
value string
expected []string
}{
{

View File

@@ -1,5 +1,4 @@
LOAD check_identifier 0
RELOAD check_identifier
MAP check_identifier
MOUT back 0
MOUT quit 9

View File

@@ -1 +1 @@
Failed to connect to the custodial service.Please try again.
Failed to connect to the custodial service. Please try again.

View File

@@ -1,5 +1,7 @@
LOAD reset_api_call_failure 6
RELOAD reset_api_call_failure
MOUT retry 1
MOUT quit 9
HALT
INCMP _ 1
INCMP ^ 1
INCMP quit 9

View File

@@ -0,0 +1 @@
Imeshindwa kuunganisha kwenye huduma ya uangalizi. Tafadhali jaribu tena.

View File

@@ -1,5 +1,6 @@
LOAD check_transactions 0
RELOAD check_transactions
CATCH api_failure flag_api_call_error 1
CATCH no_transfers flag_no_transfers 1
LOAD authorize_account 6
MOUT back 0

View File

@@ -31,6 +31,11 @@ msgstr "Salio la Kikundi: 0.00"
msgid "Symbol: %s\nBalance: %s"
msgstr "Sarafu: %s\nSalio: %s"
msgid "Your request has been sent. You will receive an SMS when your %s %s has been swapped for %s."
msgstr "Ombi lako limetumwa. Utapokea SMS wakati %s %s yako itakapobadilishwa kuwa %s."
msgid "%s balance: %s\n"
msgstr "%s salio: %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."

View File

@@ -0,0 +1 @@
Available amount {{.swap_max_limit}} is too low, please try again:

View File

@@ -0,0 +1,6 @@
MAP swap_max_limit
MOUT retry 1
MOUT quit 9
HALT
INCMP _ 1
INCMP quit 9

View File

@@ -0,0 +1 @@
Kiasi kinachopatikana {{.swap_max_limit}} ni cha chini sana, tafadhali jaribu tena:

View File

@@ -2,19 +2,22 @@ LOAD clear_temporary_value 2
RELOAD clear_temporary_value
LOAD manage_vouchers 160
RELOAD manage_vouchers
CATCH api_failure flag_api_call_error 1
LOAD check_balance 128
RELOAD check_balance
CATCH api_failure flag_api_call_error 1
MAP check_balance
MOUT send 1
MOUT vouchers 2
MOUT account 3
MOUT help 4
MOUT swap 2
MOUT vouchers 3
MOUT account 4
MOUT help 5
MOUT quit 9
HALT
INCMP send 1
INCMP my_vouchers 2
INCMP my_account 3
INCMP help 4
INCMP swap_to_list 2
INCMP my_vouchers 3
INCMP my_account 4
INCMP help 5
INCMP quit 9
INCMP . *

View File

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

View File

@@ -0,0 +1,6 @@
MAP swap_to_list
MOUT back 0
MOUT quit 9
HALT
INCMP ^ 0
INCMP quit 9

View File

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

View File

@@ -5,4 +5,5 @@ HALT
INCMP _ 0
LOAD request_custom_alias 0
RELOAD request_custom_alias
CATCH api_failure flag_api_call_error 1
INCMP confirm_new_alias *

View File

@@ -31,4 +31,7 @@ flag,flag_back_set,37,this is set when it is a back navigation
flag,flag_account_blocked,38,this is set when an account has been blocked after the allowed incorrect PIN attempts have been exceeded
flag,flag_invalid_pin,39,this is set when the given PIN is invalid(is less than or more than 4 digits)
flag,flag_alias_set,40,this is set when an account alias has been assigned to a user
flag,flag_account_pin_reset,41,this is set on an account when an admin triggers a PIN reset for them
flag,flag_account_pin_reset,41,this is set on an account when an admin triggers a PIN reset for themflag,flag_incorrect_pool,39,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
1 flag flag_language_set 8 checks whether the user has set their prefered language
31 flag flag_account_blocked 38 this is set when an account has been blocked after the allowed incorrect PIN attempts have been exceeded
32 flag flag_invalid_pin 39 this is set when the given PIN is invalid(is less than or more than 4 digits)
33 flag flag_alias_set 40 this is set when an account alias has been assigned to a user
34 flag flag_account_pin_reset 41 this is set on an account when an admin triggers a PIN reset for them this is set on an account when an admin triggers a PIN reset for themflag
35 flag flag_incorrect_pool 42 this is set when the user selects an invalid pool
36 flag flag_low_swap_amount 43 this is set when the swap max limit is less than 0.1
37

View File

@@ -3,14 +3,14 @@ LOAD get_vouchers 0
MAP get_vouchers
MOUT back 0
MOUT quit 99
MNEXT next 11
MPREV prev 22
MNEXT next 88
MPREV prev 98
HALT
INCMP > 88
INCMP < 98
INCMP _ 0
INCMP quit 99
LOAD view_voucher 80
RELOAD view_voucher
CATCH . flag_incorrect_voucher 1
INCMP _ 0
INCMP quit 99
INCMP > 11
INCMP < 22
INCMP view_voucher *

View File

@@ -5,6 +5,7 @@ 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

View File

@@ -0,0 +1,3 @@
LOAD reset_incorrect 6
LOAD initiate_swap 0
HALT

View File

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

View File

@@ -0,0 +1,6 @@
RELOAD swap_max_limit
MAP swap_max_limit
MOUT back 0
HALT
INCMP _ 0
INCMP swap_preview *

View File

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

View File

@@ -0,0 +1 @@
Swap

View File

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

View File

@@ -0,0 +1,12 @@
LOAD swap_preview 0
MAP swap_preview
MOUT back 0
MOUT quit 9
LOAD authorize_account 6
HALT
RELOAD authorize_account
CATCH incorrect_pin flag_incorrect_pin 1
CATCH . flag_account_authorized 0
INCMP _ 0
INCMP quit 9
INCMP swap_initiated *

View File

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

View File

@@ -0,0 +1,2 @@
Select number or symbol to swap TO:
{{.swap_to_list}}

View File

@@ -0,0 +1,12 @@
LOAD swap_to_list 0
RELOAD swap_to_list
MAP swap_to_list
CATCH missing_voucher flag_incorrect_voucher 1
MOUT back 0
HALT
LOAD swap_max_limit 64
RELOAD swap_max_limit
CATCH . flag_incorrect_voucher 1
CATCH low_swap_amount flag_low_swap_amount 1
INCMP _ 0
INCMP swap_limit *

View File

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

View File

@@ -1,5 +1,7 @@
CATCH no_voucher flag_no_active_voucher 1
LOAD get_voucher_details 0
RELOAD get_voucher_details
CATCH api_failure flag_api_call_error 1
MAP get_voucher_details
MOUT back 0
HALT

View File

@@ -181,8 +181,8 @@ func (s *SshRunner) GetEngine(sessionId string) (engine.Engine, func(), error) {
accountService := services.New(ctx, menuStorageService)
_, err = lhs.GetHandler(accountService)
if err != nil {
fmt.Fprintf(os.Stderr, "get accounts service handler: %v\n", err)
os.Exit(1)
fmt.Fprintf(os.Stderr, "get accounts service handler: %v\n", err)
os.Exit(1)
}
en := lhs.GetEngine(lhs.Cfg, rs, pe)
closer := func() {

View File

@@ -67,6 +67,24 @@ const (
DATA_SUGGESTED_ALIAS
//Key used to store a value of 1 for a user to reset their own PIN once they access the menu.
DATA_SELF_PIN_RESET
// Holds the active pool contract address for the swap
DATA_ACTIVE_POOL_ADDRESS
// Currently active swap from symbol for the swap
DATA_ACTIVE_SWAP_FROM_SYM
// Currently active swap from decimal count for the swap
DATA_ACTIVE_SWAP_FROM_DECIMAL
// Holds the active swap from contract address for the swap
DATA_ACTIVE_SWAP_FROM_ADDRESS
// Currently active swap from to for the swap
DATA_ACTIVE_SWAP_TO_SYM
// Currently active swap to decimal count for the swap
DATA_ACTIVE_SWAP_TO_DECIMAL
// Holds the active pool contract address for the swap
DATA_ACTIVE_SWAP_TO_ADDRESS
// Holds the max swap amount for the swap
DATA_ACTIVE_SWAP_MAX_AMOUNT
// Holds the active swap amount for the swap
DATA_ACTIVE_SWAP_AMOUNT
)
const (
@@ -105,6 +123,31 @@ const (
DATA_TRANSACTIONS = 1024 + iota
)
const (
// List of voucher symbols in the top pools context.
DATA_POOL_NAMES = 2048 + iota
// List of symbols in the top pools context.
DATA_POOL_SYMBOLS
// List of contact addresses in the top pools context
DATA_POOL_ADDRESSES
// List of swap from voucher symbols in the user context.
DATA_POOL_FROM_SYMBOLS
// List of swap from balances for vouchers valid in the pools context.
DATA_POOL_FROM_BALANCES
// List of swap from decimal counts for vouchers valid in the pools context.
DATA_POOL_FROM_DECIMALS
// List of swap from EVM addresses for vouchers valid in the pools context.
DATA_POOL_FROM_ADDRESSES
// List of swap to voucher symbols in the user context.
DATA_POOL_TO_SYMBOLS
// List of swap to balances for vouchers valid in the pools context.
DATA_POOL_TO_BALANCES
// List of swap to decimal counts for vouchers valid in the pools context.
DATA_POOL_TO_DECIMALS
// List of swap to EVM addresses for vouchers valid in the pools context.
DATA_POOL_TO_ADDRESSES
)
var (
logg = logging.NewVanilla().WithDomain("urdt-common")
)

27
store/log_db.go Normal file
View File

@@ -0,0 +1,27 @@
package store
import (
"context"
visedb "git.defalsify.org/vise.git/db"
"git.grassecon.net/grassrootseconomics/sarafu-vise/store/db"
storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db"
)
type LogDb struct {
visedb.Db
}
func (db *LogDb) WriteLogEntry(ctx context.Context, sessionId string, typ db.DataTyp, v []byte) error {
db.SetPrefix(visedb.DATATYPE_USERDATA)
db.SetSession(sessionId)
k := storedb.ToBytes(typ)
return db.Put(ctx, k, v)
}
func (db *LogDb) ReadLogEntry(ctx context.Context, sessionId string, typ db.DataTyp) ([]byte, error) {
db.SetPrefix(visedb.DATATYPE_USERDATA)
db.SetSession(sessionId)
k := storedb.ToBytes(typ)
return db.Get(ctx, k)
}

93
store/pools.go Normal file
View File

@@ -0,0 +1,93 @@
package store
import (
"context"
"fmt"
"strings"
storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
)
// PoolsMetadata helps organize data fields
type PoolsMetadata struct {
PoolNames string
PoolSymbols string
PoolContractAdrresses string
}
// ProcessPools converts pools into formatted strings
func ProcessPools(pools []dataserviceapi.PoolDetails) PoolsMetadata {
var data PoolsMetadata
var poolNames, poolSymbols, poolContractAdrresses []string
for i, p := range pools {
poolNames = append(poolNames, fmt.Sprintf("%d:%s", i+1, p.PoolName))
poolSymbols = append(poolSymbols, fmt.Sprintf("%d:%s", i+1, p.PoolSymbol))
poolContractAdrresses = append(poolContractAdrresses, fmt.Sprintf("%d:%s", i+1, p.PoolContractAdrress))
}
data.PoolNames = strings.Join(poolNames, "\n")
data.PoolSymbols = strings.Join(poolSymbols, "\n")
data.PoolContractAdrresses = strings.Join(poolContractAdrresses, "\n")
return data
}
// GetPoolData retrieves and matches pool data
// if no match is found, it fetches the API with the symbol
func GetPoolData(ctx context.Context, store DataStore, sessionId string, input string) (*dataserviceapi.PoolDetails, error) {
keys := []storedb.DataTyp{
storedb.DATA_POOL_NAMES,
storedb.DATA_POOL_SYMBOLS,
storedb.DATA_POOL_ADDRESSES,
}
data := make(map[storedb.DataTyp]string)
for _, key := range keys {
value, err := store.ReadEntry(ctx, sessionId, key)
if err != nil {
return nil, fmt.Errorf("failed to get data key %x: %v", key, err)
}
data[key] = string(value)
}
name, symbol, address := MatchPool(input,
data[storedb.DATA_POOL_NAMES],
data[storedb.DATA_POOL_SYMBOLS],
data[storedb.DATA_POOL_ADDRESSES],
)
if symbol == "" {
return nil, nil
}
return &dataserviceapi.PoolDetails{
PoolName: string(name),
PoolSymbol: string(symbol),
PoolContractAdrress: string(address),
}, nil
}
// MatchPool finds the matching pool name, symbol and pool contract address based on the input.
func MatchPool(input, names, symbols, addresses string) (name, symbol, address string) {
nameList := strings.Split(names, "\n")
symList := strings.Split(symbols, "\n")
addrList := strings.Split(addresses, "\n")
for i, sym := range symList {
parts := strings.SplitN(sym, ":", 2)
if input == parts[0] || strings.EqualFold(input, parts[1]) {
symbol = parts[1]
if i < len(nameList) {
name = strings.SplitN(nameList[i], ":", 2)[1]
}
if i < len(addrList) {
address = strings.SplitN(addrList[i], ":", 2)[1]
}
break
}
}
return
}

187
store/swap.go Normal file
View File

@@ -0,0 +1,187 @@
package store
import (
"context"
"errors"
"fmt"
"reflect"
storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
)
type SwapData struct {
PublicKey string
ActivePoolAddress string
ActiveSwapFromSym string
ActiveSwapFromDecimal string
ActiveSwapFromAddress string
ActiveSwapToSym string
ActiveSwapToAddress string
}
type SwapPreviewData struct {
PublicKey string
ActiveSwapMaxAmount string
ActiveSwapFromDecimal string
ActivePoolAddress string
ActiveSwapFromAddress string
ActiveSwapFromSym string
ActiveSwapToAddress string
ActiveSwapToSym string
ActiveSwapToDecimal string
}
func ReadSwapData(ctx context.Context, store DataStore, sessionId string) (SwapData, error) {
data := SwapData{}
fieldToKey := map[string]storedb.DataTyp{
"PublicKey": storedb.DATA_PUBLIC_KEY,
"ActivePoolAddress": storedb.DATA_ACTIVE_POOL_ADDRESS,
"ActiveSwapFromSym": storedb.DATA_ACTIVE_SYM,
"ActiveSwapFromDecimal": storedb.DATA_ACTIVE_DECIMAL,
"ActiveSwapFromAddress": storedb.DATA_ACTIVE_ADDRESS,
"ActiveSwapToSym": storedb.DATA_ACTIVE_SWAP_TO_SYM,
"ActiveSwapToAddress": storedb.DATA_ACTIVE_SWAP_TO_ADDRESS,
}
v := reflect.ValueOf(&data).Elem()
for fieldName, key := range fieldToKey {
field := v.FieldByName(fieldName)
if !field.IsValid() || !field.CanSet() {
return data, errors.New("invalid struct field: " + fieldName)
}
value, err := ReadStringEntry(ctx, store, sessionId, key)
if err != nil {
return data, err
}
field.SetString(value)
}
return data, nil
}
func ReadSwapPreviewData(ctx context.Context, store DataStore, sessionId string) (SwapPreviewData, error) {
data := SwapPreviewData{}
fieldToKey := map[string]storedb.DataTyp{
"PublicKey": storedb.DATA_PUBLIC_KEY,
"ActiveSwapMaxAmount": storedb.DATA_ACTIVE_SWAP_MAX_AMOUNT,
"ActiveSwapFromDecimal": storedb.DATA_ACTIVE_DECIMAL,
"ActivePoolAddress": storedb.DATA_ACTIVE_POOL_ADDRESS,
"ActiveSwapFromAddress": storedb.DATA_ACTIVE_ADDRESS,
"ActiveSwapFromSym": storedb.DATA_ACTIVE_SYM,
"ActiveSwapToAddress": storedb.DATA_ACTIVE_SWAP_TO_ADDRESS,
"ActiveSwapToSym": storedb.DATA_ACTIVE_SWAP_TO_SYM,
"ActiveSwapToDecimal": storedb.DATA_ACTIVE_SWAP_TO_DECIMAL,
}
v := reflect.ValueOf(&data).Elem()
for fieldName, key := range fieldToKey {
field := v.FieldByName(fieldName)
if !field.IsValid() || !field.CanSet() {
return data, errors.New("invalid struct field: " + fieldName)
}
value, err := ReadStringEntry(ctx, store, sessionId, key)
if err != nil {
return data, err
}
field.SetString(value)
}
return data, nil
}
// GetSwapFromVoucherData retrieves and matches swap from voucher data
func GetSwapFromVoucherData(ctx context.Context, store DataStore, sessionId string, input string) (*dataserviceapi.TokenHoldings, error) {
keys := []storedb.DataTyp{
storedb.DATA_POOL_FROM_SYMBOLS,
storedb.DATA_POOL_FROM_BALANCES,
storedb.DATA_POOL_FROM_DECIMALS,
storedb.DATA_POOL_FROM_ADDRESSES,
}
data := make(map[storedb.DataTyp]string)
for _, key := range keys {
value, err := store.ReadEntry(ctx, sessionId, key)
if err != nil {
return nil, fmt.Errorf("failed to get data key %x: %v", key, err)
}
data[key] = string(value)
}
symbol, balance, decimal, address := MatchVoucher(input,
data[storedb.DATA_POOL_FROM_SYMBOLS],
data[storedb.DATA_POOL_FROM_BALANCES],
data[storedb.DATA_POOL_FROM_DECIMALS],
data[storedb.DATA_POOL_FROM_ADDRESSES],
)
if symbol == "" {
return nil, nil
}
return &dataserviceapi.TokenHoldings{
TokenSymbol: string(symbol),
Balance: string(balance),
TokenDecimals: string(decimal),
ContractAddress: string(address),
}, nil
}
// GetSwapToVoucherData retrieves and matches voucher data
func GetSwapToVoucherData(ctx context.Context, store DataStore, sessionId string, input string) (*dataserviceapi.TokenHoldings, error) {
keys := []storedb.DataTyp{
storedb.DATA_POOL_TO_SYMBOLS,
storedb.DATA_POOL_TO_BALANCES,
storedb.DATA_POOL_TO_DECIMALS,
storedb.DATA_POOL_TO_ADDRESSES,
}
data := make(map[storedb.DataTyp]string)
for _, key := range keys {
value, err := store.ReadEntry(ctx, sessionId, key)
if err != nil {
return nil, fmt.Errorf("failed to get data key %x: %v", key, err)
}
data[key] = string(value)
}
symbol, balance, decimal, address := MatchVoucher(input,
data[storedb.DATA_POOL_TO_SYMBOLS],
data[storedb.DATA_POOL_TO_BALANCES],
data[storedb.DATA_POOL_TO_DECIMALS],
data[storedb.DATA_POOL_TO_ADDRESSES],
)
if symbol == "" {
return nil, nil
}
return &dataserviceapi.TokenHoldings{
TokenSymbol: string(symbol),
Balance: string(balance),
TokenDecimals: string(decimal),
ContractAddress: string(address),
}, nil
}
// UpdateSwapToVoucherData updates the active swap to voucher data in the DataStore.
func UpdateSwapToVoucherData(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error {
logg.TraceCtxf(ctx, "dtal", "data", data)
// Active swap to voucher data entries
activeEntries := map[storedb.DataTyp][]byte{
storedb.DATA_ACTIVE_SWAP_TO_SYM: []byte(data.TokenSymbol),
storedb.DATA_ACTIVE_SWAP_TO_DECIMAL: []byte(data.TokenDecimals),
storedb.DATA_ACTIVE_SWAP_TO_ADDRESS: []byte(data.ContractAddress),
}
// Write active data
for key, value := range activeEntries {
if err := store.WriteEntry(ctx, sessionId, key, value); err != nil {
return err
}
}
return nil
}

146
store/swap_test.go Normal file
View File

@@ -0,0 +1,146 @@
package store
import (
"testing"
storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db"
"github.com/alecthomas/assert/v2"
)
func TestReadSwapData(t *testing.T) {
sessionId := "session123"
publicKey := "0X13242618721"
ctx, store := InitializeTestDb(t)
// Test swap data
swapData := map[storedb.DataTyp]string{
storedb.DATA_PUBLIC_KEY: publicKey,
storedb.DATA_ACTIVE_POOL_ADDRESS: "0x48a953cA5cf5298bc6f6Af3C608351f537AAcb9e",
storedb.DATA_ACTIVE_SWAP_FROM_SYM: "AMANI",
storedb.DATA_ACTIVE_SWAP_FROM_DECIMAL: "6",
storedb.DATA_ACTIVE_SWAP_FROM_ADDRESS: "0xc7B78Ac9ACB9E025C8234621FC515bC58179dEAe",
storedb.DATA_ACTIVE_SWAP_TO_SYM: "cUSD",
storedb.DATA_ACTIVE_SWAP_TO_ADDRESS: "0x765DE816845861e75A25fCA122bb6898B8B1282a",
}
// Store the data
for key, value := range swapData {
if err := store.WriteEntry(ctx, sessionId, key, []byte(value)); err != nil {
t.Fatal(err)
}
}
expectedResult := SwapData{
PublicKey: "0X13242618721",
ActivePoolAddress: "0x48a953cA5cf5298bc6f6Af3C608351f537AAcb9e",
ActiveSwapFromSym: "AMANI",
ActiveSwapFromDecimal: "6",
ActiveSwapFromAddress: "0xc7B78Ac9ACB9E025C8234621FC515bC58179dEAe",
ActiveSwapToSym: "cUSD",
ActiveSwapToAddress: "0x765DE816845861e75A25fCA122bb6898B8B1282a",
}
data, err := ReadSwapData(ctx, store, sessionId)
assert.NoError(t, err)
assert.Equal(t, expectedResult, data)
}
func TestReadSwapPreviewData(t *testing.T) {
sessionId := "session123"
publicKey := "0X13242618721"
ctx, store := InitializeTestDb(t)
// Test swap preview data
swapPreviewData := map[storedb.DataTyp]string{
storedb.DATA_PUBLIC_KEY: publicKey,
storedb.DATA_ACTIVE_SWAP_MAX_AMOUNT: "1339482",
storedb.DATA_ACTIVE_SWAP_FROM_DECIMAL: "6",
storedb.DATA_ACTIVE_POOL_ADDRESS: "0x48a953cA5cf5298bc6f6Af3C608351f537AAcb9e",
storedb.DATA_ACTIVE_SWAP_FROM_ADDRESS: "0xc7B78Ac9ACB9E025C8234621FC515bC58179dEAe",
storedb.DATA_ACTIVE_SWAP_FROM_SYM: "AMANI",
storedb.DATA_ACTIVE_SWAP_TO_ADDRESS: "0x765DE816845861e75A25fCA122bb6898B8B1282a",
storedb.DATA_ACTIVE_SWAP_TO_SYM: "cUSD",
storedb.DATA_ACTIVE_SWAP_TO_DECIMAL: "18",
}
// Store the data
for key, value := range swapPreviewData {
if err := store.WriteEntry(ctx, sessionId, key, []byte(value)); err != nil {
t.Fatal(err)
}
}
expectedResult := SwapPreviewData{
PublicKey: "0X13242618721",
ActiveSwapMaxAmount: "1339482",
ActiveSwapFromDecimal: "6",
ActivePoolAddress: "0x48a953cA5cf5298bc6f6Af3C608351f537AAcb9e",
ActiveSwapFromAddress: "0xc7B78Ac9ACB9E025C8234621FC515bC58179dEAe",
ActiveSwapFromSym: "AMANI",
ActiveSwapToAddress: "0x765DE816845861e75A25fCA122bb6898B8B1282a",
ActiveSwapToSym: "cUSD",
ActiveSwapToDecimal: "18",
}
data, err := ReadSwapPreviewData(ctx, store, sessionId)
assert.NoError(t, err)
assert.Equal(t, expectedResult, data)
}
func TestGetSwapFromVoucherData(t *testing.T) {
sessionId := "session123"
ctx, store := InitializeTestDb(t)
// Test pool swap data
mockData := map[storedb.DataTyp][]byte{
storedb.DATA_POOL_FROM_SYMBOLS: []byte("1:AMANI\n2:AMUA"),
storedb.DATA_POOL_FROM_BALANCES: []byte("1:\n2:"),
storedb.DATA_POOL_FROM_DECIMALS: []byte("1:6\n2:4"),
storedb.DATA_POOL_FROM_ADDRESSES: []byte("1:0xc7B78Ac9ACB9E025C8234621FC515bC58179dEAe\n2:0xF0C3C7581b8b96B59a97daEc8Bd48247cE078674"),
}
// Put the data
for key, value := range mockData {
if err := store.WriteEntry(ctx, sessionId, key, []byte(value)); err != nil {
t.Fatal(err)
}
}
result, err := GetSwapFromVoucherData(ctx, store, sessionId, "1")
assert.NoError(t, err)
assert.Equal(t, "AMANI", result.TokenSymbol)
assert.Equal(t, "", result.Balance)
assert.Equal(t, "6", result.TokenDecimals)
assert.Equal(t, "0xc7B78Ac9ACB9E025C8234621FC515bC58179dEAe", result.ContractAddress)
}
func TestGetSwapToVoucherData(t *testing.T) {
sessionId := "session123"
ctx, store := InitializeTestDb(t)
// Test pool swap data
mockData := map[storedb.DataTyp][]byte{
storedb.DATA_POOL_TO_SYMBOLS: []byte("1:cUSD\n2:AMUA"),
storedb.DATA_POOL_TO_BALANCES: []byte("1:\n2:"),
storedb.DATA_POOL_TO_DECIMALS: []byte("1:6\n2:4"),
storedb.DATA_POOL_TO_ADDRESSES: []byte("1:0xc7B78Ac9ACB9E025C8234621\n2:0xF0C3C7581b8b96B59a97daEc8Bd48247cE078674"),
}
// Put the data
for key, value := range mockData {
if err := store.WriteEntry(ctx, sessionId, key, []byte(value)); err != nil {
t.Fatal(err)
}
}
result, err := GetSwapToVoucherData(ctx, store, sessionId, "1")
assert.NoError(t, err)
assert.Equal(t, "cUSD", result.TokenSymbol)
assert.Equal(t, "", result.Balance)
assert.Equal(t, "6", result.TokenDecimals)
assert.Equal(t, "0xc7B78Ac9ACB9E025C8234621", result.ContractAddress)
}

View File

@@ -64,7 +64,7 @@ func ReadTransactionData(ctx context.Context, store DataStore, sessionId string)
return data, errors.New("invalid struct field: " + fieldName)
}
value, err := readStringEntry(ctx, store, sessionId, key)
value, err := ReadStringEntry(ctx, store, sessionId, key)
if err != nil {
return data, err
}
@@ -74,7 +74,7 @@ func ReadTransactionData(ctx context.Context, store DataStore, sessionId string)
return data, nil
}
func readStringEntry(ctx context.Context, store DataStore, sessionId string, key storedb.DataTyp) (string, error) {
func ReadStringEntry(ctx context.Context, store DataStore, sessionId string, key storedb.DataTyp) (string, error) {
entry, err := store.ReadEntry(ctx, sessionId, key)
if err != nil {
return "", err

View File

@@ -113,6 +113,12 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool, *persist.Pe
os.Exit(1)
}
logdb, err := menuStorageService.GetLogDb(ctx, userDataStore, "test-db-logs", "user-data")
if err != nil {
fmt.Fprintf(os.Stderr, "get log db error: %v\n", err)
os.Exit(1)
}
dbResource, ok := rs.(*resource.DbResource)
if !ok {
fmt.Fprintf(os.Stderr, "dbresource cast error")
@@ -121,6 +127,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool, *persist.Pe
lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
lhs.SetDataStore(&userDataStore)
lhs.SetLogDb(&logdb)
lhs.SetPersister(pe)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
@@ -154,6 +161,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool, *persist.Pe
en := lhs.GetEngine(lhs.Cfg, rs, pe)
cleanFn := func() {
logdb.Close(ctx)
err := en.Finish(ctx)
if err != nil {
logg.Errorf(err.Error())