hotfix/v1.2.5-rc.1 #82
| @ -21,3 +21,11 @@ LANGUAGES=eng, swa | |||||||
| 
 | 
 | ||||||
| #Alias search domains | #Alias search domains | ||||||
| ALIAS_SEARCH_DOMAINS=sarafu.local, sarafu.eth | 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 | ||||||
|  | |||||||
| @ -52,7 +52,6 @@ func SearchDomains() []string { | |||||||
| 	return ParsedDomains | 	return ParsedDomains | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| func Language() string { | func Language() string { | ||||||
| 	return viseconfig.DefaultLanguage | 	return viseconfig.DefaultLanguage | ||||||
| } | } | ||||||
| @ -76,3 +75,15 @@ func PortSSH() uint { | |||||||
| func ATEndpoint() string { | func ATEndpoint() string { | ||||||
| 	return env.GetEnv("AT_ENDPOINT", "/") | 	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", "") | ||||||
|  | } | ||||||
|  | |||||||
| @ -15,7 +15,7 @@ import ( | |||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| 	changeHeadSrc = `LOAD reset_account_authorized 0 | 	changeHeadSrc = `LOAD reset_account_authorized 0 | ||||||
| LOAD reset_incorrect 0 | LOAD reset_incorrect_pin 0 | ||||||
| CATCH incorrect_pin flag_incorrect_pin 1 | CATCH incorrect_pin flag_incorrect_pin 1 | ||||||
| CATCH pin_entry flag_account_authorized 0 | CATCH pin_entry flag_account_authorized 0 | ||||||
| ` | ` | ||||||
|  | |||||||
							
								
								
									
										4
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.mod
									
									
									
									
									
								
							| @ -5,12 +5,12 @@ go 1.23.4 | |||||||
| require ( | require ( | ||||||
| 	git.defalsify.org/vise.git v0.3.2-0.20250528124150-03bf7bfc1b66 | 	git.defalsify.org/vise.git v0.3.2-0.20250528124150-03bf7bfc1b66 | ||||||
| 	git.grassecon.net/grassrootseconomics/common v0.9.0-beta.1.0.20250417111317-2953f4c2f32e | 	git.grassecon.net/grassrootseconomics/common v0.9.0-beta.1.0.20250417111317-2953f4c2f32e | ||||||
| 	git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250428082711-5d221b8d565f | 	git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250606194235-b5ccaea57560 | ||||||
| 	git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306 | 	git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306 | ||||||
| 	git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694 | 	git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694 | ||||||
| 	github.com/alecthomas/assert/v2 v2.2.2 | 	github.com/alecthomas/assert/v2 v2.2.2 | ||||||
| 	github.com/gofrs/uuid v4.4.0+incompatible | 	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.7-beta | ||||||
| 	github.com/jackc/pgx/v5 v5.7.1 | 	github.com/jackc/pgx/v5 v5.7.1 | ||||||
| 	github.com/peteole/testdata-loader v0.3.0 | 	github.com/peteole/testdata-loader v0.3.0 | ||||||
| 	github.com/stretchr/testify v1.9.0 | 	github.com/stretchr/testify v1.9.0 | ||||||
|  | |||||||
							
								
								
									
										24
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								go.sum
									
									
									
									
									
								
							| @ -10,6 +10,24 @@ git.grassecon.net/grassrootseconomics/common v0.9.0-beta.1.0.20250417111317-2953 | |||||||
| git.grassecon.net/grassrootseconomics/common v0.9.0-beta.1.0.20250417111317-2953f4c2f32e/go.mod h1:wgQJZGIS6QuNLHqDhcsvehsbn5PvgV7aziRebMnJi60= | 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 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.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/sarafu-api v0.9.0-beta.1.0.20250521141246-6c3719e3b652 h1:MmUQRL6Fjq/jlL53h+cUbJ7WwQ+q/yNy/zT05c7bgNg= | ||||||
|  | git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250521141246-6c3719e3b652/go.mod h1:wKHPy1mpOCr9ahkRltwm1yi9qH/3m9xb8hMCX5C0L1o= | ||||||
|  | git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250522123108-24224e553de5 h1:7gVnkpybzg5lC7C8Rl4dejTbmBVpu5xfMMfda+d498U= | ||||||
|  | git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250522123108-24224e553de5/go.mod h1:wKHPy1mpOCr9ahkRltwm1yi9qH/3m9xb8hMCX5C0L1o= | ||||||
|  | git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250605174108-bf830e92dea2 h1:8cxGb7lSoNGJxjauIRGskp//EqisuOOZPoPj4oDwswE= | ||||||
|  | git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250605174108-bf830e92dea2/go.mod h1:wKHPy1mpOCr9ahkRltwm1yi9qH/3m9xb8hMCX5C0L1o= | ||||||
|  | git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250606194235-b5ccaea57560 h1:14QVGEgLdl1LyVS/mJFDVRGXHsH5IgmloNPKfk26K6M= | ||||||
|  | git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250606194235-b5ccaea57560/go.mod h1:wKHPy1mpOCr9ahkRltwm1yi9qH/3m9xb8hMCX5C0L1o= | ||||||
| git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306 h1:Jo+yWysWw/N5BJQtAyEMN8ePVvAyPHv+JG4lQti+1N4= | git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306 h1:Jo+yWysWw/N5BJQtAyEMN8ePVvAyPHv+JG4lQti+1N4= | ||||||
| git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306/go.mod h1:FdLwYtzsjOIcDiW4uDgDYnB4Wdzq12uJUe0QHSSPbSo= | git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306/go.mod h1:FdLwYtzsjOIcDiW4uDgDYnB4Wdzq12uJUe0QHSSPbSo= | ||||||
| git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694 h1:DjJlBSz0S13acft5XZDWk7ZYnzElym0xLMYEVgyNJ+E= | git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694 h1:DjJlBSz0S13acft5XZDWk7ZYnzElym0xLMYEVgyNJ+E= | ||||||
| @ -68,6 +86,12 @@ github.com/grassrootseconomics/ethutils v1.3.1 h1:LlQO90HjJkl7ejC8fv6jP7RJUrAm1j | |||||||
| github.com/grassrootseconomics/ethutils v1.3.1/go.mod h1:Wuv1VEZrkLIXqTSEYI3Nh9HG/ZHOUQ+U+xvWJ8QtjgQ= | 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 h1:fn1gwbWIwHVEBtUC2zi5OqTlfI/5gU1SMk0fgGixIXk= | ||||||
| github.com/grassrootseconomics/ussd-data-service v1.2.0-beta/go.mod h1:omfI0QtUwIdpu9gMcUqLMCG8O1XWjqJGBx1qUMiGWC0= | 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/grassrootseconomics/ussd-data-service v1.4.7-beta h1:yAe1YaOBsdxW2m20jnVU4F0kLmFr+mK/gHCWEdHmE90= | ||||||
|  | github.com/grassrootseconomics/ussd-data-service v1.4.7-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 h1:U4kkNYryi/qfbBF8gh7Vsbuz+cVmhf5kt6pE9bYYyLo= | ||||||
| github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4/go.mod h1:zpZDgZFzeq9s0MIeB1P50NIEWDFFHSFBohI/NbaTD/Y= | 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 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= | ||||||
|  | |||||||
| @ -630,21 +630,11 @@ func (h *MenuHandlers) incrementIncorrectPINAttempts(ctx context.Context, sessio | |||||||
| // resetIncorrectPINAttempts resets the number of incorrect PIN attempts after a correct PIN entry
 | // resetIncorrectPINAttempts resets the number of incorrect PIN attempts after a correct PIN entry
 | ||||||
| func (h *MenuHandlers) resetIncorrectPINAttempts(ctx context.Context, sessionId string) error { | func (h *MenuHandlers) resetIncorrectPINAttempts(ctx context.Context, sessionId string) error { | ||||||
| 	store := h.userdataStore | 	store := h.userdataStore | ||||||
| 	currentWrongPinAttempts, err := store.ReadEntry(ctx, sessionId, storedb.DATA_INCORRECT_PIN_ATTEMPTS) | 	err := store.WriteEntry(ctx, sessionId, storedb.DATA_INCORRECT_PIN_ATTEMPTS, []byte(string("0"))) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if db.IsNotFound(err) { | 		logg.ErrorCtxf(ctx, "failed to reset incorrect PIN attempts ", "key", storedb.DATA_INCORRECT_PIN_ATTEMPTS, "error", err) | ||||||
| 			return nil |  | ||||||
| 		} |  | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	currentWrongPinAttemptsCount, _ := strconv.ParseUint(string(currentWrongPinAttempts), 0, 64) |  | ||||||
| 	if currentWrongPinAttemptsCount <= uint64(pin.AllowedPINAttempts) { |  | ||||||
| 		err = store.WriteEntry(ctx, sessionId, storedb.DATA_INCORRECT_PIN_ATTEMPTS, []byte(string("0"))) |  | ||||||
| 		if err != nil { |  | ||||||
| 			logg.ErrorCtxf(ctx, "failed to reset incorrect PIN attempts ", "key", storedb.DATA_INCORRECT_PIN_ATTEMPTS, "value", pin.AllowedPINAttempts, "error", err) |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -1371,7 +1361,13 @@ func (h *MenuHandlers) Authorize(ctx context.Context, sym string, input []byte) | |||||||
| 	flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") | 	flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") | ||||||
| 	flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") | 	flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") | ||||||
| 	flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") | 	flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") | ||||||
| 	flag_invalid_pin, _ := h.flagManager.GetFlag("flag_invalid_pin") | 
 | ||||||
|  | 	pinInput := string(input) | ||||||
|  | 
 | ||||||
|  | 	if !pin.IsValidPIN(pinInput) { | ||||||
|  | 		res.FlagReset = append(res.FlagReset, flag_account_authorized, flag_allow_update) | ||||||
|  | 		return res, nil | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	store := h.userdataStore | 	store := h.userdataStore | ||||||
| 	AccountPin, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACCOUNT_PIN) | 	AccountPin, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACCOUNT_PIN) | ||||||
| @ -1379,40 +1375,28 @@ func (h *MenuHandlers) Authorize(ctx context.Context, sym string, input []byte) | |||||||
| 		logg.ErrorCtxf(ctx, "failed to read AccountPin entry with", "key", storedb.DATA_ACCOUNT_PIN, "error", err) | 		logg.ErrorCtxf(ctx, "failed to read AccountPin entry with", "key", storedb.DATA_ACCOUNT_PIN, "error", err) | ||||||
| 		return res, err | 		return res, err | ||||||
| 	} | 	} | ||||||
| 	str := string(input) | 
 | ||||||
| 	_, err = strconv.Atoi(str) | 	// verify that the user provided the correct PIN
 | ||||||
| 	if len(input) == 4 && err == nil { | 	if pin.VerifyPIN(string(AccountPin), pinInput) { | ||||||
| 		if pin.VerifyPIN(string(AccountPin), string(input)) { | 		// set the required flags for a valid PIN
 | ||||||
| 			if h.st.MatchFlag(flag_account_authorized, false) { |  | ||||||
| 				res.FlagReset = append(res.FlagReset, flag_incorrect_pin) |  | ||||||
| 		res.FlagSet = append(res.FlagSet, flag_allow_update, flag_account_authorized) | 		res.FlagSet = append(res.FlagSet, flag_allow_update, flag_account_authorized) | ||||||
|  | 		res.FlagReset = append(res.FlagReset, flag_incorrect_pin) | ||||||
|  | 
 | ||||||
| 		err := h.resetIncorrectPINAttempts(ctx, sessionId) | 		err := h.resetIncorrectPINAttempts(ctx, sessionId) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return res, err | 			return res, err | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 				res.FlagSet = append(res.FlagSet, flag_allow_update) | 		// set the required flags for an incorrect PIN
 | ||||||
| 				res.FlagReset = append(res.FlagReset, flag_account_authorized) | 		res.FlagSet = append(res.FlagSet, flag_incorrect_pin) | ||||||
| 				err := h.resetIncorrectPINAttempts(ctx, sessionId) | 		res.FlagReset = append(res.FlagReset, flag_account_authorized, flag_allow_update) | ||||||
| 				if err != nil { | 
 | ||||||
| 					return res, err |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 		err = h.incrementIncorrectPINAttempts(ctx, sessionId) | 		err = h.incrementIncorrectPINAttempts(ctx, sessionId) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return res, err | 			return res, err | ||||||
| 		} | 		} | ||||||
| 			res.FlagSet = append(res.FlagSet, flag_incorrect_pin) |  | ||||||
| 			res.FlagReset = append(res.FlagReset, flag_account_authorized) |  | ||||||
| 			return res, nil |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		if string(input) != "0" { |  | ||||||
| 			res.FlagSet = append(res.FlagSet, flag_invalid_pin) |  | ||||||
| 		} |  | ||||||
| 		return res, nil |  | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
| 	return res, nil | 	return res, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -2261,6 +2245,110 @@ func (h *MenuHandlers) GetVoucherDetails(ctx context.Context, sym string, input | |||||||
| 	return res, nil | 	return res, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // GetDefaultPool returns the current user's Pool. If none is set, it returns the default config pool.
 | ||||||
|  | func (h *MenuHandlers) GetDefaultPool(ctx context.Context, sym string, input []byte) (resource.Result, error) { | ||||||
|  | 	var res resource.Result | ||||||
|  | 	sessionId, ok := ctx.Value("SessionId").(string) | ||||||
|  | 	if !ok { | ||||||
|  | 		return res, fmt.Errorf("missing session") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	userStore := h.userdataStore | ||||||
|  | 	activePoolSym, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_POOL_SYM) | ||||||
|  | 	if err != nil { | ||||||
|  | 		if db.IsNotFound(err) { | ||||||
|  | 			// set the default as the response
 | ||||||
|  | 			res.Content = config.DefaultPoolSymbol() | ||||||
|  | 			return res, nil | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		logg.ErrorCtxf(ctx, "failed to read the activePoolSym entry with", "key", storedb.DATA_ACTIVE_POOL_SYM, "error", err) | ||||||
|  | 		return res, err | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	res.Content = string(activePoolSym)  | ||||||
|  | 
 | ||||||
|  | 	return res, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ViewPool retrieves the pool details from the user store
 | ||||||
|  | // and displays it to the user for them to select it.
 | ||||||
|  | func (h *MenuHandlers) ViewPool(ctx context.Context, sym string, input []byte) (resource.Result, error) { | ||||||
|  | 	var res resource.Result | ||||||
|  | 	sessionId, ok := ctx.Value("SessionId").(string) | ||||||
|  | 	if !ok { | ||||||
|  | 		return res, fmt.Errorf("missing session") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	code := codeFromCtx(ctx) | ||||||
|  | 	l := gotext.NewLocale(translationDir, code) | ||||||
|  | 	l.AddDomain("default") | ||||||
|  | 
 | ||||||
|  | 	flag_incorrect_pool, _ := h.flagManager.GetFlag("flag_incorrect_pool") | ||||||
|  | 
 | ||||||
|  | 	inputStr := string(input) | ||||||
|  | 
 | ||||||
|  | 	poolData, err := store.GetPoolData(ctx, h.userdataStore, sessionId, inputStr) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return res, fmt.Errorf("failed to retrieve pool data: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if poolData == nil { | ||||||
|  | 		flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") | ||||||
|  | 
 | ||||||
|  | 		// no match found. Call the API using the inputStr as the symbol
 | ||||||
|  | 		poolResp, err := h.accountService.RetrievePoolDetails(ctx, inputStr) | ||||||
|  | 		if err != nil { | ||||||
|  | 			res.FlagSet = append(res.FlagSet, flag_api_error) | ||||||
|  | 			return res, nil | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if len(poolResp.PoolSymbol) == 0 { | ||||||
|  | 			// If the API does not return the data, set the flag
 | ||||||
|  | 			res.FlagSet = append(res.FlagSet, flag_incorrect_pool) | ||||||
|  | 			return res, nil | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		poolData = poolResp | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := store.StoreTemporaryPool(ctx, h.userdataStore, sessionId, poolData); err != nil { | ||||||
|  | 		logg.ErrorCtxf(ctx, "failed on StoreTemporaryPool", "error", err) | ||||||
|  | 		return res, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	res.FlagReset = append(res.FlagReset, flag_incorrect_pool) | ||||||
|  | 	res.Content = l.Get("Name: %s\nSymbol: %s", poolData.PoolName, poolData.PoolSymbol) | ||||||
|  | 
 | ||||||
|  | 	return res, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // SetPool retrieves the temp pool data and sets it as the active data.
 | ||||||
|  | func (h *MenuHandlers) SetPool(ctx context.Context, sym string, input []byte) (resource.Result, error) { | ||||||
|  | 	var res resource.Result | ||||||
|  | 
 | ||||||
|  | 	sessionId, ok := ctx.Value("SessionId").(string) | ||||||
|  | 	if !ok { | ||||||
|  | 		return res, fmt.Errorf("missing session") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Get temporary data
 | ||||||
|  | 	tempData, err := store.GetTemporaryPoolData(ctx, h.userdataStore, sessionId) | ||||||
|  | 	if err != nil { | ||||||
|  | 		logg.ErrorCtxf(ctx, "failed on GetTemporaryPoolData", "error", err) | ||||||
|  | 		return res, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Set as active and clear temporary data
 | ||||||
|  | 	if err := store.UpdatePoolData(ctx, h.userdataStore, sessionId, tempData); err != nil { | ||||||
|  | 		logg.ErrorCtxf(ctx, "failed on UpdatePoolData", "error", err) | ||||||
|  | 		return res, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	res.Content = tempData.PoolSymbol | ||||||
|  | 	return res, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // CheckTransactions retrieves the transactions from the API using the "PublicKey" and stores to prefixDb.
 | // CheckTransactions retrieves the transactions from the API using the "PublicKey" and stores to prefixDb.
 | ||||||
| func (h *MenuHandlers) CheckTransactions(ctx context.Context, sym string, input []byte) (resource.Result, error) { | func (h *MenuHandlers) CheckTransactions(ctx context.Context, sym string, input []byte) (resource.Result, error) { | ||||||
| 	var res resource.Result | 	var res resource.Result | ||||||
| @ -2651,3 +2739,383 @@ func (h *MenuHandlers) ClearTemporaryValue(ctx context.Context, sym string, inpu | |||||||
| 	} | 	} | ||||||
| 	return res, nil | 	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 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Get active pool address or fall back to default
 | ||||||
|  | 	var activePoolAddress []byte | ||||||
|  | 	activePoolAddress, err = userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_POOL_ADDRESS) | ||||||
|  | 	if err != nil { | ||||||
|  | 		if db.IsNotFound(err) { | ||||||
|  | 			defaultPoolAddress := config.DefaultPoolAddress() | ||||||
|  | 			// store the default as the active pool address
 | ||||||
|  | 			err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_POOL_ADDRESS, []byte(defaultPoolAddress)) | ||||||
|  | 			if err != nil { | ||||||
|  | 				logg.ErrorCtxf(ctx, "failed to write default PoolContractAdrress", "key", storedb.DATA_ACTIVE_POOL_ADDRESS, "value", defaultPoolAddress, "error", err) | ||||||
|  | 				return res, err | ||||||
|  | 			} | ||||||
|  | 			activePoolAddress = []byte(defaultPoolAddress) | ||||||
|  | 		} else { | ||||||
|  | 			logg.ErrorCtxf(ctx, "failed to read active PoolContractAdrress", "key", storedb.DATA_ACTIVE_POOL_ADDRESS, "error", err) | ||||||
|  | 			return res, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// call the api using the ActivePoolAddress and ActiveVoucherAddress to check if it is part of the pool
 | ||||||
|  | 	r, err := h.accountService.CheckTokenInPool(ctx, string(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.ProcessTokens(swapToList) | ||||||
|  | 
 | ||||||
|  | 	// Store all swap_to tokens 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, 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 | ||||||
|  | 	} | ||||||
|  | 	// store the user's input amount in the temporary value
 | ||||||
|  | 	err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(inputStr)) | ||||||
|  | 	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.", | ||||||
|  | 		swapData.TemporaryValue, | ||||||
|  | 		swapData.ActiveSwapFromSym, | ||||||
|  | 		swapData.ActiveSwapToSym, | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 	res.FlagReset = append(res.FlagReset, flag_account_authorized) | ||||||
|  | 	return res, nil | ||||||
|  | } | ||||||
|  | |||||||
| @ -1116,7 +1116,6 @@ func TestAuthorize(t *testing.T) { | |||||||
| 	flag_incorrect_pin, _ := fm.GetFlag("flag_incorrect_pin") | 	flag_incorrect_pin, _ := fm.GetFlag("flag_incorrect_pin") | ||||||
| 	flag_account_authorized, _ := fm.GetFlag("flag_account_authorized") | 	flag_account_authorized, _ := fm.GetFlag("flag_account_authorized") | ||||||
| 	flag_allow_update, _ := fm.GetFlag("flag_allow_update") | 	flag_allow_update, _ := fm.GetFlag("flag_allow_update") | ||||||
| 	flag_invalid_pin, _ := fm.GetFlag("flag_invalid_pin") |  | ||||||
| 
 | 
 | ||||||
| 	// Set 1234 is the correct account pin
 | 	// Set 1234 is the correct account pin
 | ||||||
| 	accountPIN := "1234" | 	accountPIN := "1234" | ||||||
| @ -1134,7 +1133,7 @@ func TestAuthorize(t *testing.T) { | |||||||
| 		expectedResult resource.Result | 		expectedResult resource.Result | ||||||
| 	}{ | 	}{ | ||||||
| 		{ | 		{ | ||||||
| 			name:  "Test with correct pin", | 			name:  "Test with correct PIN", | ||||||
| 			input: []byte("1234"), | 			input: []byte("1234"), | ||||||
| 			expectedResult: resource.Result{ | 			expectedResult: resource.Result{ | ||||||
| 				FlagReset: []uint32{flag_incorrect_pin}, | 				FlagReset: []uint32{flag_incorrect_pin}, | ||||||
| @ -1142,18 +1141,18 @@ func TestAuthorize(t *testing.T) { | |||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name:  "Test with incorrect pin", | 			name:  "Test with incorrect PIN", | ||||||
| 			input: []byte("1235"), | 			input: []byte("1235"), | ||||||
| 			expectedResult: resource.Result{ | 			expectedResult: resource.Result{ | ||||||
| 				FlagReset: []uint32{flag_account_authorized}, | 				FlagReset: []uint32{flag_account_authorized, flag_allow_update}, | ||||||
| 				FlagSet:   []uint32{flag_incorrect_pin}, | 				FlagSet:   []uint32{flag_incorrect_pin}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name:  "Test with pin that is not a 4 digit", | 			name:  "Test with PIN that is not a 4 digit", | ||||||
| 			input: []byte("1235aqds"), | 			input: []byte("1235aqds"), | ||||||
| 			expectedResult: resource.Result{ | 			expectedResult: resource.Result{ | ||||||
| 				FlagSet: []uint32{flag_invalid_pin}, | 				FlagReset: []uint32{flag_account_authorized, flag_allow_update}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -91,7 +91,7 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountService) | |||||||
| 	ls.DbRs.AddLocalFunc("get_recipient", appHandlers.GetRecipient) | 	ls.DbRs.AddLocalFunc("get_recipient", appHandlers.GetRecipient) | ||||||
| 	ls.DbRs.AddLocalFunc("get_sender", appHandlers.GetSender) | 	ls.DbRs.AddLocalFunc("get_sender", appHandlers.GetSender) | ||||||
| 	ls.DbRs.AddLocalFunc("get_amount", appHandlers.GetAmount) | 	ls.DbRs.AddLocalFunc("get_amount", appHandlers.GetAmount) | ||||||
| 	ls.DbRs.AddLocalFunc("reset_incorrect", appHandlers.ResetIncorrectPin) | 	ls.DbRs.AddLocalFunc("reset_incorrect_pin", appHandlers.ResetIncorrectPin) | ||||||
| 	ls.DbRs.AddLocalFunc("save_firstname", appHandlers.SaveFirstname) | 	ls.DbRs.AddLocalFunc("save_firstname", appHandlers.SaveFirstname) | ||||||
| 	ls.DbRs.AddLocalFunc("save_familyname", appHandlers.SaveFamilyname) | 	ls.DbRs.AddLocalFunc("save_familyname", appHandlers.SaveFamilyname) | ||||||
| 	ls.DbRs.AddLocalFunc("save_gender", appHandlers.SaveGender) | 	ls.DbRs.AddLocalFunc("save_gender", appHandlers.SaveGender) | ||||||
| @ -112,6 +112,10 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountService) | |||||||
| 	ls.DbRs.AddLocalFunc("view_voucher", appHandlers.ViewVoucher) | 	ls.DbRs.AddLocalFunc("view_voucher", appHandlers.ViewVoucher) | ||||||
| 	ls.DbRs.AddLocalFunc("set_voucher", appHandlers.SetVoucher) | 	ls.DbRs.AddLocalFunc("set_voucher", appHandlers.SetVoucher) | ||||||
| 	ls.DbRs.AddLocalFunc("get_voucher_details", appHandlers.GetVoucherDetails) | 	ls.DbRs.AddLocalFunc("get_voucher_details", appHandlers.GetVoucherDetails) | ||||||
|  | 	ls.DbRs.AddLocalFunc("get_default_pool", appHandlers.GetDefaultPool) | ||||||
|  | 	ls.DbRs.AddLocalFunc("get_pools", appHandlers.GetPools) | ||||||
|  | 	ls.DbRs.AddLocalFunc("view_pool", appHandlers.ViewPool) | ||||||
|  | 	ls.DbRs.AddLocalFunc("set_pool", appHandlers.SetPool) | ||||||
| 	ls.DbRs.AddLocalFunc("validate_blocked_number", appHandlers.ValidateBlockedNumber) | 	ls.DbRs.AddLocalFunc("validate_blocked_number", appHandlers.ValidateBlockedNumber) | ||||||
| 	ls.DbRs.AddLocalFunc("retrieve_blocked_number", appHandlers.RetrieveBlockedNumber) | 	ls.DbRs.AddLocalFunc("retrieve_blocked_number", appHandlers.RetrieveBlockedNumber) | ||||||
| 	ls.DbRs.AddLocalFunc("reset_unregistered_number", appHandlers.ResetUnregisteredNumber) | 	ls.DbRs.AddLocalFunc("reset_unregistered_number", appHandlers.ResetUnregisteredNumber) | ||||||
| @ -130,7 +134,10 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountService) | |||||||
| 	ls.DbRs.AddLocalFunc("confirm_new_alias", appHandlers.ConfirmNewAlias) | 	ls.DbRs.AddLocalFunc("confirm_new_alias", appHandlers.ConfirmNewAlias) | ||||||
| 	ls.DbRs.AddLocalFunc("check_account_created", appHandlers.CheckAccountCreated) | 	ls.DbRs.AddLocalFunc("check_account_created", appHandlers.CheckAccountCreated) | ||||||
| 	ls.DbRs.AddLocalFunc("reset_api_call_failure", appHandlers.ResetApiCallFailure) | 	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 | 	ls.first = appHandlers.Init | ||||||
| 
 | 
 | ||||||
| 	return appHandlers, nil | 	return appHandlers, nil | ||||||
|  | |||||||
| @ -1 +1 @@ | |||||||
| Failed to connect to the custodial service .Please try again. | Your request failed. Please try again later. | ||||||
| @ -1 +1 @@ | |||||||
| Imeshindwa kuunganisha kwenye huduma ya uangalizi. Tafadhali jaribu tena. | Ombi lako halikufaulu. Tafadhali jaribu tena baadaye. | ||||||
| @ -1,5 +1,5 @@ | |||||||
| LOAD reset_account_authorized 0 | LOAD reset_account_authorized 0 | ||||||
| LOAD reset_incorrect 0 | LOAD reset_incorrect_pin 0 | ||||||
| CATCH incorrect_pin flag_incorrect_pin 1 | CATCH incorrect_pin flag_incorrect_pin 1 | ||||||
| CATCH pin_entry flag_account_authorized 0 | CATCH pin_entry flag_account_authorized 0 | ||||||
| MOUT english 1 | MOUT english 1 | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| LOAD reset_incorrect 6 | LOAD reset_incorrect_pin 6 | ||||||
| LOAD fetch_community_balance 0 | LOAD fetch_community_balance 0 | ||||||
| CATCH api_failure flag_api_call_error 1 | CATCH api_failure flag_api_call_error 1 | ||||||
| MAP fetch_community_balance | MAP fetch_community_balance | ||||||
|  | |||||||
| @ -1 +1 @@ | |||||||
| Incorrect PIN. You have: {{.reset_incorrect}} remaining attempt(s). | Incorrect PIN. You have: {{.reset_incorrect_pin}} remaining attempt(s). | ||||||
| @ -1,6 +1,6 @@ | |||||||
| LOAD reset_incorrect 0 | LOAD reset_incorrect_pin 0 | ||||||
| RELOAD reset_incorrect | RELOAD reset_incorrect_pin | ||||||
| MAP reset_incorrect | MAP reset_incorrect_pin | ||||||
| CATCH blocked_account flag_account_blocked 1 | CATCH blocked_account flag_account_blocked 1 | ||||||
| MOUT retry 1 | MOUT retry 1 | ||||||
| MOUT quit 9 | MOUT quit 9 | ||||||
|  | |||||||
| @ -1 +1 @@ | |||||||
| PIN ulioeka sio sahihi, una majaribio: {{.reset_incorrect}} yaliyobaki | PIN ulioeka sio sahihi, una majaribio: {{.reset_incorrect_pin}} yaliyobaki | ||||||
| @ -31,6 +31,14 @@ msgstr "Salio la Kikundi: 0.00" | |||||||
| msgid "Symbol: %s\nBalance: %s" | msgid "Symbol: %s\nBalance: %s" | ||||||
| msgstr "Sarafu: %s\nSalio: %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" | 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." | ||||||
|  | 
 | ||||||
|  | msgid "Name: %s\nSymbol: %s" | ||||||
|  | msgstr "Jina: %s\nSarafu: %s" | ||||||
							
								
								
									
										1
									
								
								services/registration/low_swap_amount
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/low_swap_amount
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | Available amount {{.swap_max_limit}} is too low, please try again: | ||||||
							
								
								
									
										6
									
								
								services/registration/low_swap_amount.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								services/registration/low_swap_amount.vis
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | MAP swap_max_limit | ||||||
|  | MOUT retry 1 | ||||||
|  | MOUT quit 9 | ||||||
|  | HALT | ||||||
|  | INCMP _ 1 | ||||||
|  | INCMP quit 9 | ||||||
							
								
								
									
										1
									
								
								services/registration/low_swap_amount_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/low_swap_amount_swa
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | Kiasi kinachopatikana {{.swap_max_limit}} ni cha chini sana, tafadhali jaribu tena: | ||||||
| @ -7,14 +7,16 @@ LOAD check_balance 128 | |||||||
| RELOAD check_balance | RELOAD check_balance | ||||||
| MAP check_balance | MAP check_balance | ||||||
| MOUT send 1 | MOUT send 1 | ||||||
| MOUT vouchers 2 | MOUT swap 2 | ||||||
| MOUT account 3 | MOUT vouchers 3 | ||||||
| MOUT help 4 | MOUT account 4 | ||||||
|  | MOUT help 5 | ||||||
| MOUT quit 9 | MOUT quit 9 | ||||||
| HALT | HALT | ||||||
| INCMP send 1 | INCMP send 1 | ||||||
| INCMP my_vouchers 2 | INCMP swap_to_list 2 | ||||||
| INCMP my_account 3 | INCMP my_vouchers 3 | ||||||
| INCMP help 4 | INCMP my_account 4 | ||||||
|  | INCMP help 5 | ||||||
| INCMP quit 9 | INCMP quit 9 | ||||||
| INCMP . * | INCMP . * | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								services/registration/missing_voucher
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/missing_voucher
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | {{.swap_to_list}} | ||||||
							
								
								
									
										6
									
								
								services/registration/missing_voucher.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								services/registration/missing_voucher.vis
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | MAP swap_to_list | ||||||
|  | MOUT back 0 | ||||||
|  | MOUT quit 9 | ||||||
|  | HALT | ||||||
|  | INCMP ^ 0 | ||||||
|  | INCMP quit 9 | ||||||
							
								
								
									
										1
									
								
								services/registration/missing_voucher_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/missing_voucher_swa
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | {{.swap_to_list}} | ||||||
| @ -1,4 +1,4 @@ | |||||||
| LOAD reset_incorrect 6 | LOAD reset_incorrect_pin 6 | ||||||
| LOAD check_balance 0 | LOAD check_balance 0 | ||||||
| CATCH api_failure flag_api_call_error 1 | CATCH api_failure flag_api_call_error 1 | ||||||
| MAP check_balance | MAP check_balance | ||||||
|  | |||||||
| @ -2,8 +2,11 @@ LOAD reset_account_authorized 16 | |||||||
| RELOAD reset_account_authorized | RELOAD reset_account_authorized | ||||||
| MOUT select_voucher 1 | MOUT select_voucher 1 | ||||||
| MOUT voucher_details 2 | MOUT voucher_details 2 | ||||||
|  | MOUT select_pool 3 | ||||||
| MOUT back 0 | MOUT back 0 | ||||||
| HALT | HALT | ||||||
| INCMP _ 0 | INCMP _ 0 | ||||||
| INCMP select_voucher 1 | INCMP select_voucher 1 | ||||||
| INCMP voucher_details 2 | INCMP voucher_details 2 | ||||||
|  | INCMP select_pool 3 | ||||||
|  | INCMP . * | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| RELOAD reset_incorrect | RELOAD reset_incorrect_pin | ||||||
| MOUT back 0 | MOUT back 0 | ||||||
| HALT | HALT | ||||||
| INCMP _ 0 | INCMP _ 0 | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ LOAD set_back 6 | |||||||
| LOAD authorize_account 16 | LOAD authorize_account 16 | ||||||
| LOAD reset_allow_update 4 | LOAD reset_allow_update 4 | ||||||
| LOAD save_temporary_pin 1 | LOAD save_temporary_pin 1 | ||||||
| LOAD reset_incorrect 0 | LOAD reset_incorrect_pin 0 | ||||||
| LOAD reset_invalid_pin 6 | LOAD reset_invalid_pin 6 | ||||||
| MOUT change_pin 1 | MOUT change_pin 1 | ||||||
| MOUT reset_pin 2 | MOUT reset_pin 2 | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								services/registration/pool_set
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/pool_set
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | Success! {{.set_pool}} is now your active pool. | ||||||
							
								
								
									
										10
									
								
								services/registration/pool_set.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								services/registration/pool_set.vis
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | LOAD reset_incorrect_pin 6 | ||||||
|  | CATCH _ flag_account_authorized 0 | ||||||
|  | LOAD set_pool 20 | ||||||
|  | MAP set_pool | ||||||
|  | MOUT back 0 | ||||||
|  | MOUT quit 9 | ||||||
|  | HALT | ||||||
|  | INCMP ^ 0 | ||||||
|  | INCMP quit 9 | ||||||
|  | INCMP ^ * | ||||||
							
								
								
									
										1
									
								
								services/registration/pool_set_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/pool_set_swa
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | Hongera! {{.set_pool}} ni bwawa la Sarafu linalotumika sasa. | ||||||
| @ -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_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_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_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 | ||||||
|  | 
 | ||||||
|  | |||||||
| Can't render this file because it has a wrong number of fields in line 34. | 
							
								
								
									
										3
									
								
								services/registration/select_pool
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								services/registration/select_pool
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | Enter number or symbol to set the default pool: | ||||||
|  | Current: {{.get_default_pool}} | ||||||
|  | {{.get_pools}} | ||||||
							
								
								
									
										20
									
								
								services/registration/select_pool.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								services/registration/select_pool.vis
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | |||||||
|  | CATCH no_voucher flag_no_active_voucher 1 | ||||||
|  | LOAD get_pools 0 | ||||||
|  | MAP get_pools | ||||||
|  | LOAD get_default_pool 20 | ||||||
|  | RELOAD get_default_pool | ||||||
|  | MAP get_default_pool | ||||||
|  | MOUT back 0 | ||||||
|  | MOUT quit 99 | ||||||
|  | MNEXT next 88 | ||||||
|  | MPREV prev 98 | ||||||
|  | HALT | ||||||
|  | INCMP > 88 | ||||||
|  | INCMP < 98 | ||||||
|  | INCMP _ 0 | ||||||
|  | INCMP quit 99 | ||||||
|  | LOAD view_pool 80 | ||||||
|  | RELOAD view_pool | ||||||
|  | CATCH api_failure flag_api_call_error 1 | ||||||
|  | CATCH . flag_incorrect_pool 1 | ||||||
|  | INCMP view_pool * | ||||||
							
								
								
									
										1
									
								
								services/registration/select_pool_menu
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/select_pool_menu
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | Select pool | ||||||
							
								
								
									
										1
									
								
								services/registration/select_pool_menu_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/select_pool_menu_swa
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | Chagua Bwawa | ||||||
							
								
								
									
										3
									
								
								services/registration/select_pool_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								services/registration/select_pool_swa
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | Chagua nambari au ishara kuweka bwawa la sarafu: | ||||||
|  | La sasa: {{.get_default_pool}} | ||||||
|  | {{.get_pools}} | ||||||
							
								
								
									
										4
									
								
								services/registration/swap_initiated.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								services/registration/swap_initiated.vis
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | |||||||
|  | LOAD reset_incorrect_pin 6 | ||||||
|  | CATCH _ flag_account_authorized 0 | ||||||
|  | LOAD initiate_swap 0 | ||||||
|  | HALT | ||||||
							
								
								
									
										1
									
								
								services/registration/swap_limit
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/swap_limit
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | {{.swap_max_limit}} | ||||||
							
								
								
									
										5
									
								
								services/registration/swap_limit.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								services/registration/swap_limit.vis
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | MAP swap_max_limit | ||||||
|  | MOUT back 0 | ||||||
|  | HALT | ||||||
|  | INCMP _ 0 | ||||||
|  | INCMP swap_preview * | ||||||
							
								
								
									
										1
									
								
								services/registration/swap_limit_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/swap_limit_swa
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | {{.swap_max_limit}} | ||||||
							
								
								
									
										1
									
								
								services/registration/swap_menu
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/swap_menu
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | Swap | ||||||
							
								
								
									
										3
									
								
								services/registration/swap_preview
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								services/registration/swap_preview
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | {{.swap_preview}} | ||||||
|  | 
 | ||||||
|  | Please enter your PIN to confirm: | ||||||
							
								
								
									
										12
									
								
								services/registration/swap_preview.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								services/registration/swap_preview.vis
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | LOAD swap_preview 0 | ||||||
|  | MAP swap_preview | ||||||
|  | CATCH api_failure flag_api_call_error 1 | ||||||
|  | MOUT back 0 | ||||||
|  | MOUT quit 9 | ||||||
|  | LOAD authorize_account 6 | ||||||
|  | HALT | ||||||
|  | RELOAD authorize_account | ||||||
|  | CATCH incorrect_pin flag_incorrect_pin 1 | ||||||
|  | INCMP _ 0 | ||||||
|  | INCMP quit 9 | ||||||
|  | INCMP swap_initiated * | ||||||
							
								
								
									
										3
									
								
								services/registration/swap_preview_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								services/registration/swap_preview_swa
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | {{.swap_preview}} | ||||||
|  | 
 | ||||||
|  | Tafadhali weka PIN yako kudhibitisha: | ||||||
							
								
								
									
										2
									
								
								services/registration/swap_to_list
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								services/registration/swap_to_list
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | Select number or symbol to swap TO: | ||||||
|  | {{.swap_to_list}} | ||||||
							
								
								
									
										14
									
								
								services/registration/swap_to_list.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								services/registration/swap_to_list.vis
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | CATCH no_voucher flag_no_active_voucher 1 | ||||||
|  | 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 api_failure flag_api_call_error 1 | ||||||
|  | CATCH . flag_incorrect_voucher 1 | ||||||
|  | CATCH low_swap_amount flag_low_swap_amount 1 | ||||||
|  | INCMP _ 0 | ||||||
|  | INCMP swap_limit * | ||||||
							
								
								
									
										2
									
								
								services/registration/swap_to_list_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								services/registration/swap_to_list_swa
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | Chagua nambari au ishara ya sarafu kubadilisha KWENDA: | ||||||
|  | {{.swap_to_list}} | ||||||
| @ -1,5 +1,4 @@ | |||||||
| LOAD reset_incorrect 6 | LOAD reset_incorrect_pin 6 | ||||||
| CATCH incorrect_pin flag_incorrect_pin 1 |  | ||||||
| CATCH _ flag_account_authorized 0 | CATCH _ flag_account_authorized 0 | ||||||
| RELOAD get_amount | RELOAD get_amount | ||||||
| MAP get_amount | MAP get_amount | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								services/registration/view_pool
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								services/registration/view_pool
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | Enter PIN to confirm selection: | ||||||
|  | {{.view_pool}} | ||||||
							
								
								
									
										10
									
								
								services/registration/view_pool.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								services/registration/view_pool.vis
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | MAP view_pool | ||||||
|  | MOUT back 0 | ||||||
|  | MOUT quit 9 | ||||||
|  | LOAD authorize_account 6 | ||||||
|  | HALT | ||||||
|  | RELOAD authorize_account | ||||||
|  | CATCH incorrect_pin flag_incorrect_pin 1 | ||||||
|  | INCMP _ 0 | ||||||
|  | INCMP quit 9 | ||||||
|  | INCMP pool_set * | ||||||
							
								
								
									
										2
									
								
								services/registration/view_pool_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								services/registration/view_pool_swa
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | Weka PIN ili kuthibitisha chaguo: | ||||||
|  | {{.view_pool}} | ||||||
| @ -1,6 +1,6 @@ | |||||||
| LOAD get_profile_info 0 | LOAD get_profile_info 0 | ||||||
| MAP get_profile_info  | MAP get_profile_info  | ||||||
| LOAD reset_incorrect 6 | LOAD reset_incorrect_pin 6 | ||||||
| CATCH incorrect_pin flag_incorrect_pin 1 | CATCH incorrect_pin flag_incorrect_pin 1 | ||||||
| CATCH pin_entry flag_account_authorized 0 | CATCH pin_entry flag_account_authorized 0 | ||||||
| MOUT back 0 | MOUT back 0 | ||||||
|  | |||||||
| @ -1,5 +1,4 @@ | |||||||
| LOAD reset_incorrect 6 | LOAD reset_incorrect_pin 6 | ||||||
| CATCH incorrect_pin flag_incorrect_pin 1 |  | ||||||
| CATCH _ flag_account_authorized 0 | CATCH _ flag_account_authorized 0 | ||||||
| LOAD set_voucher 12 | LOAD set_voucher 12 | ||||||
| MAP set_voucher | MAP set_voucher | ||||||
|  | |||||||
| @ -67,6 +67,28 @@ const ( | |||||||
| 	DATA_SUGGESTED_ALIAS | 	DATA_SUGGESTED_ALIAS | ||||||
| 	//Key used to store a value of 1 for a user to reset their own PIN once they access the menu. 
 | 	//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 | 	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 | ||||||
|  | 	// Holds the active pool name for the swap
 | ||||||
|  | 	DATA_ACTIVE_POOL_NAME | ||||||
|  | 	// Holds the active pool symbol for the swap
 | ||||||
|  | 	DATA_ACTIVE_POOL_SYM | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| @ -105,6 +127,31 @@ const ( | |||||||
| 	DATA_TRANSACTIONS = 1024 + iota | 	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 ( | var ( | ||||||
| 	logg = logging.NewVanilla().WithDomain("urdt-common") | 	logg = logging.NewVanilla().WithDomain("urdt-common") | ||||||
| ) | ) | ||||||
|  | |||||||
							
								
								
									
										142
									
								
								store/pools.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								store/pools.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,142 @@ | |||||||
|  | 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 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // StoreTemporaryPool saves pool metadata as temporary entries in the DataStore.
 | ||||||
|  | func StoreTemporaryPool(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.PoolDetails) error { | ||||||
|  | 	tempData := fmt.Sprintf("%s,%s,%s", data.PoolName, data.PoolSymbol, data.PoolContractAdrress) | ||||||
|  | 
 | ||||||
|  | 	if err := store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(tempData)); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GetTemporaryPoolData retrieves temporary pool metadata from the DataStore.
 | ||||||
|  | func GetTemporaryPoolData(ctx context.Context, store DataStore, sessionId string) (*dataserviceapi.PoolDetails, error) { | ||||||
|  | 	temp_data, err := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	values := strings.SplitN(string(temp_data), ",", 3) | ||||||
|  | 
 | ||||||
|  | 	data := &dataserviceapi.PoolDetails{} | ||||||
|  | 
 | ||||||
|  | 	data.PoolName = values[0] | ||||||
|  | 	data.PoolSymbol = values[1] | ||||||
|  | 	data.PoolContractAdrress = values[2] | ||||||
|  | 
 | ||||||
|  | 	return data, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // UpdatePoolData updates the active pool data in the DataStore.
 | ||||||
|  | func UpdatePoolData(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.PoolDetails) error { | ||||||
|  | 	logg.TraceCtxf(ctx, "dtal", "data", data) | ||||||
|  | 	// Active pool data entry
 | ||||||
|  | 	activeEntries := map[storedb.DataTyp][]byte{ | ||||||
|  | 		storedb.DATA_ACTIVE_POOL_NAME:     []byte(data.PoolName), | ||||||
|  | 		storedb.DATA_ACTIVE_POOL_SYM:     []byte(data.PoolSymbol), | ||||||
|  | 		storedb.DATA_ACTIVE_POOL_ADDRESS: []byte(data.PoolContractAdrress), | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Write active data
 | ||||||
|  | 	for key, value := range activeEntries { | ||||||
|  | 		if err := store.WriteEntry(ctx, sessionId, key, value); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										199
									
								
								store/swap.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								store/swap.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,199 @@ | |||||||
|  | package store | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"reflect" | ||||||
|  | 	"strconv" | ||||||
|  | 
 | ||||||
|  | 	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 { | ||||||
|  | 	TemporaryValue        string | ||||||
|  | 	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{ | ||||||
|  | 		"TemporaryValue":        storedb.DATA_TEMPORARY_VALUE, | ||||||
|  | 		"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 token data
 | ||||||
|  | func GetSwapToVoucherData(ctx context.Context, store DataStore, sessionId string, input string) (*dataserviceapi.TokenDetails, 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, _, 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 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	decimalInt, err := strconv.ParseUint(decimal, 0, 64) | ||||||
|  | 	if err != nil { | ||||||
|  | 		logg.ErrorCtxf(ctx, "Failed to parse decimal to Uint:", "sessionId", sessionId, "decimal", decimal, "error", err) | ||||||
|  | 		return nil, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return &dataserviceapi.TokenDetails{ | ||||||
|  | 		TokenSymbol:   string(symbol), | ||||||
|  | 		TokenDecimals: uint8(decimalInt), | ||||||
|  | 		TokenAddress:  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.TokenDetails) error { | ||||||
|  | 	logg.TraceCtxf(ctx, "UpdateSwapToVoucherData", "data", data) | ||||||
|  | 
 | ||||||
|  | 	// Convert TokenDecimals (uint8) to string
 | ||||||
|  | 	tokenDecimalsStr := strconv.FormatUint(uint64(data.TokenDecimals), 10) | ||||||
|  | 
 | ||||||
|  | 	// 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(tokenDecimalsStr), | ||||||
|  | 		storedb.DATA_ACTIVE_SWAP_TO_ADDRESS: []byte(data.TokenAddress), | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// 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
									
								
							
							
						
						
									
										146
									
								
								store/swap_test.go
									
									
									
									
									
										Normal 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) | ||||||
|  | } | ||||||
| @ -64,7 +64,7 @@ func ReadTransactionData(ctx context.Context, store DataStore, sessionId string) | |||||||
| 			return data, errors.New("invalid struct field: " + fieldName) | 			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 { | 		if err != nil { | ||||||
| 			return data, err | 			return data, err | ||||||
| 		} | 		} | ||||||
| @ -74,7 +74,7 @@ func ReadTransactionData(ctx context.Context, store DataStore, sessionId string) | |||||||
| 	return data, nil | 	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) | 	entry, err := store.ReadEntry(ctx, sessionId, key) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", err | 		return "", err | ||||||
|  | |||||||
| @ -47,6 +47,24 @@ func ProcessVouchers(holdings []dataserviceapi.TokenHoldings) VoucherMetadata { | |||||||
| 	return data | 	return data | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ProcessTokens converts swappable tokens into formatted strings
 | ||||||
|  | func ProcessTokens(holdings []dataserviceapi.TokenDetails) VoucherMetadata { | ||||||
|  | 	var data VoucherMetadata | ||||||
|  | 	var symbols, decimals, addresses []string | ||||||
|  | 
 | ||||||
|  | 	for i, h := range holdings { | ||||||
|  | 		symbols = append(symbols, fmt.Sprintf("%d:%s", i+1, h.TokenSymbol)) | ||||||
|  | 		decimals = append(decimals, fmt.Sprintf("%d:%d", i+1, h.TokenDecimals)) | ||||||
|  | 		addresses = append(addresses, fmt.Sprintf("%d:%s", i+1, h.TokenAddress)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	data.Symbols = strings.Join(symbols, "\n") | ||||||
|  | 	data.Decimals = strings.Join(decimals, "\n") | ||||||
|  | 	data.Addresses = strings.Join(addresses, "\n") | ||||||
|  | 
 | ||||||
|  | 	return data | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func ScaleDownBalance(balance, decimals string) string { | func ScaleDownBalance(balance, decimals string) string { | ||||||
| 	// Convert balance and decimals to big.Float
 | 	// Convert balance and decimals to big.Float
 | ||||||
| 	bal := new(big.Float) | 	bal := new(big.Float) | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user