Compare commits

..

37 Commits

Author SHA1 Message Date
alfred-mk
a96802f10f use a single FlagReset append statement 2025-05-30 05:09:27 +03:00
alfred-mk
3075070e14 remove unused CATCH statement for the flag_incorrect_pin 2025-05-30 05:06:29 +03:00
alfred-mk
9891a51f90 update the resetIncorrectPINAttempts to cater for edge cases with more than 3 invalid attempts 2025-05-30 05:01:58 +03:00
alfred-mk
7f9da1ec10 remove unused CATCH statement 2025-05-30 04:58:55 +03:00
alfred-mk
6a4b59cedf update the Authorize test 2025-05-30 04:58:20 +03:00
alfred-mk
4f02924cbd refactor the Authorize function to prevent double PIN requests 2025-05-30 04:57:32 +03:00
alfred-mk
b30e15caa5 remove additional RELOAD
Some checks failed
release / docker (push) Has been cancelled
2025-05-29 10:01:48 +03:00
alfred-mk
71b4eff35e add a catch for api failures 2025-05-29 10:00:44 +03:00
alfred-mk
20954d287b store the DATA_POOL_TO_BALANCES 2025-05-29 09:59:41 +03:00
alfred-mk
fac703576f updated the go-vise package to commit hash a170e8a79da067e30b3c28a999a4d87fdd07ebaf
Some checks failed
release / docker (push) Has been cancelled
2025-05-23 15:30:48 +03:00
alfred-mk
df8e0b4d4a remove misplaced CATCH statement 2025-05-23 15:28:40 +03:00
alfred-mk
b4bacfdc03 use the updated sarafu-api package with correct PoolSwapURL
Some checks failed
release / docker (push) Has been cancelled
2025-05-22 15:34:04 +03:00
alfred-mk
8dc203d111 use the TemporaryValue to store the user's swap amount input
Some checks failed
release / docker (push) Has been cancelled
2025-05-22 15:18:52 +03:00
alfred-mk
dcadea8737 Convert TokenDecimals (uint8) to string before storing it 2025-05-22 15:09:46 +03:00
alfred-mk
71496d0015 change position of the catch statements
Some checks failed
release / docker (push) Has been cancelled
2025-05-22 12:41:49 +03:00
alfred-mk
d9fc5440c5 prevent access to the swap menu if one does not have a voucher 2025-05-22 12:24:10 +03:00
alfred-mk
0495472530 match the reset_incorrect_pin symbol to the function name 2025-05-22 12:13:50 +03:00
alfred-mk
fd43060021 update the api failure template
Some checks failed
release / docker (push) Has been cancelled
2025-05-21 18:52:05 +03:00
alfred-mk
cf6e105fb9 catch the api call error flag 2025-05-21 18:51:41 +03:00
alfred-mk
91b57fece2 use the ProcessTokens function and remove the unused DATA_POOL_TO_BALANCES 2025-05-21 18:51:10 +03:00
alfred-mk
043c79384c correctly process TokenDetails 2025-05-21 18:39:45 +03:00
alfred-mk
1cc1d00ffe added a function to ProcessTokens from the GetPoolSwappableVouchers 2025-05-21 18:19:23 +03:00
alfred-mk
d7987bf460 use the updated ussd-data-service and sarafu-api with TokenDetails 2025-05-21 18:18:15 +03:00
alfred-mk
21da4b976b use the updated sarafu-api that removes the publicKey from the GetPoolSwappableVouchers
Some checks failed
release / docker (push) Has been cancelled
2025-05-21 15:45:17 +03:00
alfred-mk
169d85e2cb added the INCLUDE_STABLES_PARAM env variable 2025-05-21 15:39:19 +03:00
alfred-mk
3ab76b3d22 use updated sarafu-api with corrected JSON mapping for checkTokenInPool and getSwapFromTokenMaxLimit
Some checks failed
release / docker (push) Has been cancelled
2025-05-20 15:53:12 +03:00
alfred-mk
b428e2e22d add log for the CheckTokenInPool
Some checks failed
release / docker (push) Has been cancelled
2025-05-20 14:02:41 +03:00
1d23a0cc4b Merge pull request 'default-env-pool' (#76) from default-env-pool into master
Some checks failed
release / docker (push) Has been cancelled
Reviewed-on: #76
2025-05-20 12:18:30 +02:00
alfred-mk
d3ad3b2db6 update the template and remove hardcoded values 2025-05-20 10:26:28 +03:00
alfred-mk
5d8fee470a use the default pool variables 2025-05-20 10:26:01 +03:00
alfred-mk
cf237c592a add the pool variables to the config 2025-05-20 10:25:27 +03:00
alfred-mk
2d8333b89a add the new env variables 2025-05-20 10:19:46 +03:00
alfred-mk
c8146ea211 alfred/pool-swap (#33)
Some checks failed
release / docker (push) Has been cancelled
Pool swap functionality

Resolves issue #24

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

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

Reviewed-on: #74
Reviewed-by: Mohamed Sohail <kamikazechaser@noreply.localhost>
Co-authored-by: alfred-mk <alfredmwaik@gmail.com>
Co-committed-by: alfred-mk <alfredmwaik@gmail.com>
2025-05-07 11:45:38 +02:00
carlos
db5d55d8e3 Merge pull request 'route-api-errors' (#46) from route-api-errors into master
Reviewed-on: #46
2025-04-29 13:55:54 +02:00
48 changed files with 1208 additions and 113 deletions

View File

@@ -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

View File

@@ -25,7 +25,7 @@ const (
defaultSSHHost string = "127.0.0.1" defaultSSHHost string = "127.0.0.1"
defaultSSHPort uint = 7122 defaultSSHPort uint = 7122
defaultHTTPHost string = "127.0.0.1" defaultHTTPHost string = "127.0.0.1"
defaultHTTPPort uint = 7123 defaultHTTPPort uint = 7123
defaultDomain = "sarafu.local" defaultDomain = "sarafu.local"
) )
@@ -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", "")
}

View File

@@ -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
` `

32
go.mod
View File

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

84
go.sum
View File

@@ -1,13 +1,35 @@
git.defalsify.org/vise.git v0.3.2-0.20250425131748-8b84f59792ce h1:Uke2jQ4wG97gQKnTzxPyBGyhosrU1IWnRNFHtKVrmrk= git.defalsify.org/vise.git v0.3.2-0.20250425131748-8b84f59792ce h1:Uke2jQ4wG97gQKnTzxPyBGyhosrU1IWnRNFHtKVrmrk=
git.defalsify.org/vise.git v0.3.2-0.20250425131748-8b84f59792ce/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= git.defalsify.org/vise.git v0.3.2-0.20250425131748-8b84f59792ce/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
git.defalsify.org/vise.git v0.3.2-0.20250507135825-a170e8a79da0 h1:ByRD+b39zVOjOzOf3I1z2L6MSiMZCZT8y0P8MMAAorY=
git.defalsify.org/vise.git v0.3.2-0.20250507135825-a170e8a79da0/go.mod h1:flN+Gu+0BX0F6trZc5VpJcO4e6mj/+HUw0Z/UqVTFhk=
git.defalsify.org/vise.git v0.3.2-0.20250507172020-cb22240f1cb9 h1:4kjbYw25MHZe9fqSbujPzpFXrYutFfVipvLrcWYnYks=
git.defalsify.org/vise.git v0.3.2-0.20250507172020-cb22240f1cb9/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
git.grassecon.net/grassrootseconomics/common v0.9.0-beta.1.0.20250417111317-2953f4c2f32e h1:DcC9qkNl9ny3hxQmsMK6W81+5R/j4ZwYUbvewMI/rlc= git.grassecon.net/grassrootseconomics/common v0.9.0-beta.1.0.20250417111317-2953f4c2f32e h1:DcC9qkNl9ny3hxQmsMK6W81+5R/j4ZwYUbvewMI/rlc=
git.grassecon.net/grassrootseconomics/common v0.9.0-beta.1.0.20250417111317-2953f4c2f32e/go.mod h1:wgQJZGIS6QuNLHqDhcsvehsbn5PvgV7aziRebMnJi60= git.grassecon.net/grassrootseconomics/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/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=
git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694/go.mod h1:DpibtYpnT3nG4Kn556hRAkdu4+CtiI/6MbnQHal51mQ= git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694/go.mod h1:DpibtYpnT3nG4Kn556hRAkdu4+CtiI/6MbnQHal51mQ=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk=
github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
github.com/alecthomas/participle/v2 v2.0.0 h1:Fgrq+MbuSsJwIkw3fEj9h75vDP0Er5JzepJ0/HNHv0g= github.com/alecthomas/participle/v2 v2.0.0 h1:Fgrq+MbuSsJwIkw3fEj9h75vDP0Er5JzepJ0/HNHv0g=
@@ -16,21 +38,60 @@ github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c h1:H9Nm+I7Cg/YVPpEV1RzU3Wq2pjamPc/UtHDgItcb7lE= github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c h1:H9Nm+I7Cg/YVPpEV1RzU3Wq2pjamPc/UtHDgItcb7lE=
github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c/go.mod h1:rGod7o6KPeJ+hyBpHfhi4v7blx9sf+QsHsA7KAsdN6U= github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c/go.mod h1:rGod7o6KPeJ+hyBpHfhi4v7blx9sf+QsHsA7KAsdN6U=
github.com/bits-and-blooms/bitset v1.14.3 h1:Gd2c8lSNf9pKXom5JtD7AaKO8o7fGQ2LtFj1436qilA=
github.com/bits-and-blooms/bitset v1.14.3/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ=
github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M=
github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY=
github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I=
github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs=
github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI=
github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA=
github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
github.com/ethereum/go-ethereum v1.14.9 h1:J7iwXDrtUyE9FUjUYbd4c9tyzwMh6dTJsKzo9i6SrwA=
github.com/ethereum/go-ethereum v1.14.9/go.mod h1:QeW+MtTpRdBEm2pUFoonByee8zfHv7kGp0wK0odvU1I=
github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A=
github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grassrootseconomics/eth-custodial v1.3.0-beta h1:twrMBhl89GqDUL9PlkzQxMP/6OST1BByrNDj+rqXDmU= github.com/grassrootseconomics/eth-custodial v1.3.0-beta h1:twrMBhl89GqDUL9PlkzQxMP/6OST1BByrNDj+rqXDmU=
github.com/grassrootseconomics/eth-custodial v1.3.0-beta/go.mod h1:7uhRcdnJplX4t6GKCEFkbeDhhjlcaGJeJqevbcvGLZo= github.com/grassrootseconomics/eth-custodial v1.3.0-beta/go.mod h1:7uhRcdnJplX4t6GKCEFkbeDhhjlcaGJeJqevbcvGLZo=
github.com/grassrootseconomics/ethutils v1.3.1 h1:LlQO90HjJkl7ejC8fv6jP7RJUrAm1j4VMMCYfsoIrhU=
github.com/grassrootseconomics/ethutils v1.3.1/go.mod h1:Wuv1VEZrkLIXqTSEYI3Nh9HG/ZHOUQ+U+xvWJ8QtjgQ=
github.com/grassrootseconomics/ussd-data-service v1.2.0-beta h1:fn1gwbWIwHVEBtUC2zi5OqTlfI/5gU1SMk0fgGixIXk= github.com/grassrootseconomics/ussd-data-service v1.2.0-beta 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=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/holiman/uint256 v1.3.1 h1:JfTzmih28bittyHM8z360dCjIA9dbPIBlcTI6lmctQs=
github.com/holiman/uint256 v1.3.1/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
@@ -45,8 +106,13 @@ github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lmittmann/w3 v0.17.1 h1:zdXIimmNmYfqOFur+Jqc9Yqwtq6jwnsQufbTOnSAtW4=
github.com/lmittmann/w3 v0.17.1/go.mod h1:WVUGMbL83WYBu4Sge3SVlW3qIG4VaHe+S8+UUnwz9Eg=
github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a h1:0Q3H0YXzMHiciXtRcM+j0jiCe8WKPQHoRgQiRTnfcLY= github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a h1:0Q3H0YXzMHiciXtRcM+j0jiCe8WKPQHoRgQiRTnfcLY=
github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a/go.mod h1:CdTTBOYzS5E4mWS1N8NWP6AHI19MP0A2B18n3hLzRMk= github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a/go.mod h1:CdTTBOYzS5E4mWS1N8NWP6AHI19MP0A2B18n3hLzRMk=
github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU=
github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
github.com/pashagolub/pgxmock/v4 v4.3.0 h1:DqT7fk0OCK6H0GvqtcMsLpv8cIwWqdxWgfZNLeHCb/s= github.com/pashagolub/pgxmock/v4 v4.3.0 h1:DqT7fk0OCK6H0GvqtcMsLpv8cIwWqdxWgfZNLeHCb/s=
github.com/pashagolub/pgxmock/v4 v4.3.0/go.mod h1:9VoVHXwS3XR/yPtKGzwQvwZX1kzGB9sM8SviDcHDa3A= github.com/pashagolub/pgxmock/v4 v4.3.0/go.mod h1:9VoVHXwS3XR/yPtKGzwQvwZX1kzGB9sM8SviDcHDa3A=
github.com/peteole/testdata-loader v0.3.0 h1:8jckE9KcyNHgyv/VPoaljvKZE0Rqr8+dPVYH6rfNr9I= github.com/peteole/testdata-loader v0.3.0 h1:8jckE9KcyNHgyv/VPoaljvKZE0Rqr8+dPVYH6rfNr9I=
@@ -56,6 +122,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
@@ -63,18 +131,32 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4=
github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -83,3 +165,5 @@ gopkg.in/leonelquinteros/gotext.v1 v1.3.1/go.mod h1:X1WlGDeAFIYsW6GjgMm4VwUwZ2Xj
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=

View File

@@ -33,6 +33,7 @@ import (
"git.grassecon.net/grassrootseconomics/sarafu-vise/profile" "git.grassecon.net/grassrootseconomics/sarafu-vise/profile"
"git.grassecon.net/grassrootseconomics/sarafu-vise/store" "git.grassecon.net/grassrootseconomics/sarafu-vise/store"
storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db" storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db"
"github.com/grassrootseconomics/ethutils"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
) )
@@ -629,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
} }
@@ -1370,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)
@@ -1378,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.FlagSet = append(res.FlagSet, flag_allow_update, flag_account_authorized)
res.FlagReset = append(res.FlagReset, flag_incorrect_pin) res.FlagReset = append(res.FlagReset, flag_incorrect_pin)
res.FlagSet = append(res.FlagSet, flag_allow_update, flag_account_authorized)
err := h.resetIncorrectPINAttempts(ctx, sessionId) err := h.resetIncorrectPINAttempts(ctx, sessionId)
if err != nil { if err != nil {
return res, err return res, err
}
} else {
res.FlagSet = append(res.FlagSet, flag_allow_update)
res.FlagReset = append(res.FlagReset, flag_account_authorized)
err := h.resetIncorrectPINAttempts(ctx, sessionId)
if err != nil {
return res, err
}
}
} else {
err = h.incrementIncorrectPINAttempts(ctx, sessionId)
if err != nil {
return res, err
}
res.FlagSet = append(res.FlagSet, flag_incorrect_pin)
res.FlagReset = append(res.FlagReset, flag_account_authorized)
return res, nil
} }
} else { } else {
if string(input) != "0" { // set the required flags for an incorrect PIN
res.FlagSet = append(res.FlagSet, flag_invalid_pin) res.FlagSet = append(res.FlagSet, flag_incorrect_pin)
res.FlagReset = append(res.FlagReset, flag_account_authorized, flag_allow_update)
err = h.incrementIncorrectPINAttempts(ctx, sessionId)
if err != nil {
return res, err
} }
return res, nil
} }
return res, nil return res, nil
} }
@@ -1622,7 +1607,8 @@ func (h *MenuHandlers) ValidateRecipient(ctx context.Context, sym string, input
flag_invalid_recipient_with_invite, _ := h.flagManager.GetFlag("flag_invalid_recipient_with_invite") flag_invalid_recipient_with_invite, _ := h.flagManager.GetFlag("flag_invalid_recipient_with_invite")
flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
recipient := string(input) // remove white spaces
recipient := strings.ReplaceAll(string(input), " ", "")
if recipient != "0" { if recipient != "0" {
recipientType, err := identity.CheckRecipient(recipient) recipientType, err := identity.CheckRecipient(recipient)
@@ -1672,8 +1658,11 @@ func (h *MenuHandlers) ValidateRecipient(ctx context.Context, sym string, input
} }
case "address": case "address":
// checksum the address
address := ethutils.ChecksumAddress(recipient)
// Save the valid Ethereum address as the recipient // Save the valid Ethereum address as the recipient
err = store.WriteEntry(ctx, sessionId, storedb.DATA_RECIPIENT, []byte(recipient)) err = store.WriteEntry(ctx, sessionId, storedb.DATA_RECIPIENT, []byte(address))
if err != nil { if err != nil {
logg.ErrorCtxf(ctx, "failed to write recipient entry with", "key", storedb.DATA_RECIPIENT, "value", recipient, "error", err) logg.ErrorCtxf(ctx, "failed to write recipient entry with", "key", storedb.DATA_RECIPIENT, "value", recipient, "error", err)
return res, err return res, err
@@ -2176,10 +2165,6 @@ func (h *MenuHandlers) ViewVoucher(ctx context.Context, sym string, input []byte
flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher") flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher")
inputStr := string(input) inputStr := string(input)
if inputStr == "0" || inputStr == "99" {
res.FlagReset = append(res.FlagReset, flag_incorrect_voucher)
return res, nil
}
metadata, err := store.GetVoucherData(ctx, h.userdataStore, sessionId, inputStr) metadata, err := store.GetVoucherData(ctx, h.userdataStore, sessionId, inputStr)
if err != nil { if err != nil {
@@ -2650,3 +2635,381 @@ 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
}
defaultPool := dataserviceapi.PoolDetails{
PoolName: config.DefaultPoolName(),
PoolSymbol: config.DefaultPoolSymbol(),
PoolContractAdrress: config.DefaultPoolAddress(),
LimiterAddress: "",
VoucherRegistry: "",
}
activePoolAddress := defaultPool.PoolContractAdrress
// set the active pool contract address
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_POOL_ADDRESS, []byte(activePoolAddress))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write active PoolContractAdrress entry with", "key", storedb.DATA_ACTIVE_POOL_ADDRESS, "value", activePoolAddress, "error", err)
return res, err
}
// call the api using the ActivePoolAddress and ActiveVoucherAddress to check if it is part of the pool
r, err := h.accountService.CheckTokenInPool(ctx, activePoolAddress, string(activeAddress))
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_error)
logg.ErrorCtxf(ctx, "failed on CheckTokenInPool", "error", err)
return res, err
}
logg.InfoCtxf(ctx, "CheckTokenInPool", "response", r, "active_pool_address", activePoolAddress, "address", activeAddress)
if !r.CanSwapFrom {
res.FlagSet = append(res.FlagSet, flag_incorrect_voucher)
res.Content = l.Get(
"%s is not in %s. Please update your voucher and try again.",
activeSym,
config.DefaultPoolName(),
)
return res, nil
}
res.FlagReset = append(res.FlagReset, flag_incorrect_voucher)
// call the api using the activePoolAddress to get a list of SwapToSymbolsData
swapToList, err := h.accountService.GetPoolSwappableVouchers(ctx, string(activePoolAddress))
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_error)
logg.ErrorCtxf(ctx, "failed on FetchTransactions", "error", err)
return res, err
}
// Return if there are no vouchers
if len(swapToList) == 0 {
return res, nil
}
data := store.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
}

View File

@@ -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},
}, },
}, },
} }
@@ -1716,40 +1715,64 @@ func TestValidateRecipient(t *testing.T) {
// Define test cases // Define test cases
tests := []struct { tests := []struct {
name string name string
input []byte input []byte
expectedResult resource.Result expectError bool
expectedRecipient []byte
expectedResult resource.Result
}{ }{
{ {
name: "Test with invalid recepient", name: "Test with invalid recepient",
input: []byte("7?1234"), input: []byte("7?1234"),
expectError: true,
expectedResult: resource.Result{ expectedResult: resource.Result{
FlagSet: []uint32{flag_invalid_recipient}, FlagSet: []uint32{flag_invalid_recipient},
Content: "7?1234", Content: "7?1234",
}, },
}, },
{ {
name: "Test with valid unregistered recepient", name: "Test with valid unregistered recepient",
input: []byte("0712345678"), input: []byte("0712345678"),
expectError: true,
expectedResult: resource.Result{ expectedResult: resource.Result{
FlagSet: []uint32{flag_invalid_recipient_with_invite}, FlagSet: []uint32{flag_invalid_recipient_with_invite},
Content: "0712345678", Content: "0712345678",
}, },
}, },
{ {
name: "Test with valid registered recepient", name: "Test with valid registered recepient",
input: []byte("0711223344"), input: []byte("0711223344"),
expectedResult: resource.Result{}, expectError: false,
expectedRecipient: []byte(publicKey),
expectedResult: resource.Result{},
}, },
{ {
name: "Test with address", name: "Test with address",
input: []byte("0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9"), input: []byte("0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9"),
expectedResult: resource.Result{}, expectError: false,
expectedRecipient: []byte("0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9"),
expectedResult: resource.Result{},
}, },
{ {
name: "Test with alias recepient", name: "Test with alias recepient",
input: []byte("alias123.sarafu.local"), input: []byte("alias123.sarafu.local"),
expectedResult: resource.Result{}, expectError: false,
expectedRecipient: []byte("0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9"),
expectedResult: resource.Result{},
},
{
name: "Test for checksummed address",
input: []byte("0x5523058cdffe5f3c1eadadd5015e55c6e00fb439"),
expectError: false,
expectedRecipient: []byte("0x5523058cdFfe5F3c1EaDADD5015E55C6E00fb439"),
expectedResult: resource.Result{},
},
{
name: "Test with valid registered recepient that has white spaces",
input: []byte("0711 22 33 44"),
expectError: false,
expectedRecipient: []byte(publicKey),
expectedResult: resource.Result{},
}, },
} }
@@ -1782,6 +1805,12 @@ func TestValidateRecipient(t *testing.T) {
t.Error(err) t.Error(err)
} }
if !tt.expectError {
storedRecipientAddress, err := store.ReadEntry(ctx, sessionId, storedb.DATA_RECIPIENT)
assert.NoError(t, err)
assert.Equal(t, tt.expectedRecipient, storedRecipientAddress)
}
// Assert that the Result FlagSet has the required flags after language switch // Assert that the Result FlagSet has the required flags after language switch
assert.Equal(t, res, tt.expectedResult, "Result should contain flag(s) that have been reset") assert.Equal(t, res, tt.expectedResult, "Result should contain flag(s) that have been reset")
}) })

View File

@@ -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)
@@ -130,7 +130,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

View File

@@ -1 +1 @@
Failed to connect to the custodial service .Please try again. Your request failed. Please try again later.

View File

@@ -1 +1 @@
Imeshindwa kuunganisha kwenye huduma ya uangalizi. Tafadhali jaribu tena. Ombi lako halikufaulu. Tafadhali jaribu tena baadaye.

View File

@@ -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

View File

@@ -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

View File

@@ -1 +1 @@
Incorrect PIN. You have: {{.reset_incorrect}} remaining attempt(s). Incorrect PIN. You have: {{.reset_incorrect_pin}} remaining attempt(s).

View File

@@ -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

View File

@@ -1 +1 @@
PIN ulioeka sio sahihi, una majaribio: {{.reset_incorrect}} yaliyobaki PIN ulioeka sio sahihi, una majaribio: {{.reset_incorrect_pin}} yaliyobaki

View File

@@ -31,6 +31,11 @@ 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."

View File

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

View File

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

View File

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

View File

@@ -2,20 +2,21 @@ LOAD clear_temporary_value 2
RELOAD clear_temporary_value RELOAD clear_temporary_value
LOAD manage_vouchers 160 LOAD manage_vouchers 160
RELOAD manage_vouchers RELOAD manage_vouchers
CATCH api_failure flag_api_call_error 1 CATCH api_failure flag_api_call_error 1
LOAD check_balance 128 LOAD check_balance 128
RELOAD check_balance RELOAD check_balance
CATCH api_failure flag_api_call_error 1
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 . *

View File

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

View File

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

View File

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

View File

@@ -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

View File

@@ -1,4 +1,4 @@
RELOAD reset_incorrect RELOAD reset_incorrect_pin
MOUT back 0 MOUT back 0
HALT HALT
INCMP _ 0 INCMP _ 0

View File

@@ -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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
Swap

View File

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

View File

@@ -0,0 +1,12 @@
LOAD swap_preview 0
MAP swap_preview
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 *

View File

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

View File

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

View File

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

View File

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

View File

@@ -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

View File

@@ -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

View File

@@ -1,4 +1,4 @@
LOAD reset_incorrect 6 LOAD reset_incorrect_pin 6
CATCH incorrect_pin flag_incorrect_pin 1 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

View File

@@ -67,6 +67,24 @@ 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
) )
const ( const (
@@ -105,6 +123,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")
) )

93
store/pools.go Normal file
View File

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

199
store/swap.go Normal file
View 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
View File

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

View File

@@ -64,7 +64,7 @@ func ReadTransactionData(ctx context.Context, store DataStore, sessionId string)
return data, errors.New("invalid struct field: " + fieldName) 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

View File

@@ -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)