Compare commits

..

No commits in common. "master" and "handle-error-codes" have entirely different histories.

133 changed files with 557 additions and 3449 deletions

View File

@ -29,17 +29,3 @@ DEFAULT_POOL_CONTRACT_ADDRESS=0x48a953cA5cf5298bc6f6Af3C608351f537AAcb9e
DEFAULT_LIMITER_ADDRESS= DEFAULT_LIMITER_ADDRESS=
DEFAULT_VOUCHER_REGISTRY= DEFAULT_VOUCHER_REGISTRY=
INCLUDE_STABLES_PARAM=false INCLUDE_STABLES_PARAM=false
#Mpesa
DEFAULT_MPESA_ADDRESS=0x48a953cA5cf5298bc6f6Af3C608351f537AAcb9e
MIN_MPESA_SEND_AMOUNT=100
MAX_MPESA_SEND_AMOUNT=250000
MIN_MPESA_WITHDRAW_AMOUNT=20
DEFAULT_MPESA_ASSET=cUSD
MPESA_BEARER_TOKEN=eyJeSIsInRcCI6IkpXVCJ.yJwdWJsaWNLZXkiOiIwrrrrrr
MPESA_ONRAMP_BASE=https://pretium.v1.grassecon.net
# Known stable voucher addresses (USDm, USD₮)
STABLE_VOUCHER_ADDRESSES=0x765DE816845861e75A25fCA122bb6898B8B1282a,0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e
DEFAULT_STABLE_VOUCHER_ADDRESS=0x765DE816845861e75A25fCA122bb6898B8B1282a
DEFAULT_STABLE_VOUCHER_DECIMALS=18

1
.gitignore vendored
View File

@ -8,4 +8,3 @@ id_*
*.gdbm *.gdbm
*.log *.log
user-data user-data
**/*/vise-asm

View File

@ -1,4 +1,4 @@
FROM golang:1.24-bookworm AS build FROM golang:1.23.4-bookworm AS build
ENV CGO_ENABLED=1 ENV CGO_ENABLED=1
@ -11,7 +11,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
libgdbm-dev \ libgdbm-dev \
git \ git \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
RUN git clone https://github.com/nolash/go-vise go-vise RUN git clone https://git.defalsify.org/vise.git go-vise
COPY . ./sarafu-vise COPY . ./sarafu-vise
WORKDIR /build/sarafu-vise/services/registration WORKDIR /build/sarafu-vise/services/registration

View File

@ -79,12 +79,11 @@ func main() {
pfp := path.Join(scriptDir, "pp.csv") pfp := path.Join(scriptDir, "pp.csv")
cfg := engine.Config{ cfg := engine.Config{
Root: "root", Root: "root",
OutputSize: uint32(size), OutputSize: uint32(size),
FlagCount: uint32(128), FlagCount: uint32(128),
MenuSeparator: menuSeparator, MenuSeparator: menuSeparator,
ResetOnEmptyInput: true, ResetOnEmptyInput: true,
ResetRoot: true, // clear the cache once a user quits
} }
if engineDebug { if engineDebug {

View File

@ -94,12 +94,11 @@ func main() {
pfp := path.Join(scriptDir, "pp.csv") pfp := path.Join(scriptDir, "pp.csv")
cfg := engine.Config{ cfg := engine.Config{
Root: "root", Root: "root",
OutputSize: uint32(size), OutputSize: uint32(size),
FlagCount: uint32(128), FlagCount: uint32(128),
MenuSeparator: menuSeparator, MenuSeparator: menuSeparator,
ResetOnEmptyInput: true, ResetOnEmptyInput: true,
ResetRoot: true, // clear the cache once a user quits
} }
if engineDebug { if engineDebug {

View File

@ -82,7 +82,6 @@ func main() {
MenuSeparator: menuSeparator, MenuSeparator: menuSeparator,
EngineDebug: engineDebug, EngineDebug: engineDebug,
ResetOnEmptyInput: true, ResetOnEmptyInput: true,
ResetRoot: true, // clear the cache once a user quits
} }
menuStorageService := storage.NewMenuStorageService(conns) menuStorageService := storage.NewMenuStorageService(conns)

View File

@ -1,7 +1,6 @@
package config package config
import ( import (
"strconv"
"strings" "strings"
apiconfig "git.grassecon.net/grassrootseconomics/sarafu-api/config" apiconfig "git.grassecon.net/grassrootseconomics/sarafu-api/config"
@ -88,65 +87,3 @@ func DefaultPoolName() string {
func DefaultPoolSymbol() string { func DefaultPoolSymbol() string {
return env.GetEnv("DEFAULT_POOL_SYMBOL", "") return env.GetEnv("DEFAULT_POOL_SYMBOL", "")
} }
func DefaultMpesaAddress() string {
return env.GetEnv("DEFAULT_MPESA_ADDRESS", "")
}
func MinMpesaSendAmount() float64 {
v := env.GetEnv("MIN_MPESA_SEND_AMOUNT", "100")
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return 100 // fallback
}
return f
}
func MinMpesaWithdrawAmount() float64 {
v := env.GetEnv("MIN_MPESA_WITHDRAW_AMOUNT", "20")
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return 20 // fallback
}
return f
}
func MaxMpesaSendAmount() float64 {
v := env.GetEnv("MAX_MPESA_SEND_AMOUNT", "250000")
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return 250000 // fallback
}
return f
}
func DefaultMpesaAsset() string {
return env.GetEnv("DEFAULT_MPESA_ASSET", "")
}
func StableVoucherAddresses() []string {
var parsed []string
raw := env.GetEnv("STABLE_VOUCHER_ADDRESSES", "")
if raw == "" {
return parsed
}
list := strings.Split(raw, ",")
for _, addr := range list {
clean := strings.TrimSpace(addr)
if clean != "" {
parsed = append(parsed, clean)
}
}
return parsed
}
func DefaultStableVoucherAddress() string {
return env.GetEnv("DEFAULT_STABLE_VOUCHER_ADDRESS", "")
}
func DefaultStableVoucherDecimals() string {
return env.GetEnv("DEFAULT_STABLE_VOUCHER_DECIMALS", "")
}

74
go.mod
View File

@ -1,66 +1,68 @@
module git.grassecon.net/grassrootseconomics/sarafu-vise module git.grassecon.net/grassrootseconomics/sarafu-vise
go 1.24.0 go 1.23.4
toolchain go1.24.10
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.20251127132814-8ceadabbc215 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.20260207150752-71aa5ce7b537 git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251022084613-532547899f63
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/ethutils v1.6.0 github.com/grassrootseconomics/ethutils v1.3.1
github.com/grassrootseconomics/ussd-data-service v1.10.1-beta github.com/grassrootseconomics/ussd-data-service v1.6.0-beta
github.com/jackc/pgx/v5 v5.7.6 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.11.1 github.com/stretchr/testify v1.9.0
golang.org/x/crypto v0.45.0 golang.org/x/crypto v0.32.0
gopkg.in/leonelquinteros/gotext.v1 v1.3.1 gopkg.in/leonelquinteros/gotext.v1 v1.3.1
) )
require ( require (
github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251119083800-2aa1d4cc79d7 // 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 v1.0.0 // indirect github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c // indirect
github.com/bits-and-blooms/bitset v1.24.4 // indirect github.com/bits-and-blooms/bitset v1.14.3 // indirect
github.com/consensys/gnark-crypto v0.19.2 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect github.com/consensys/bavard v0.1.13 // indirect
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // 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.8.0 // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect github.com/ethereum/c-kzg-4844 v1.0.0 // indirect
github.com/ethereum/go-ethereum v1.16.7 // indirect github.com/ethereum/go-ethereum v1.14.9 // indirect
github.com/ethereum/go-verkle v0.2.2 // indirect github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/fxamacker/cbor/v2 v2.4.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect github.com/gorilla/websocket v1.5.0 // indirect
github.com/grassrootseconomics/eth-custodial v1.12.0-rc // indirect github.com/grassrootseconomics/eth-custodial v1.3.0-beta // 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.2 // 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.20.5 // 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.11+incompatible // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
github.com/stretchr/objx v0.5.3 // indirect github.com/stretchr/objx v0.5.2 // indirect
github.com/supranational/blst v0.3.16 // indirect github.com/supranational/blst v0.3.11 // indirect
github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.11.0 // 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
github.com/yusufpapurcu/wmi v1.2.4 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/sync v0.18.0 // indirect golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.38.0 // indirect golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.31.0 // indirect golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.14.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
) )

262
go.sum
View File

@ -1,169 +1,227 @@
git.defalsify.org/vise.git v0.3.2-0.20250528124150-03bf7bfc1b66 h1:hmtb2Q3lHxq+SXqG+Gn43pKhTRYx+sw5j1LpgBfXN1o= git.defalsify.org/vise.git v0.3.2-0.20250528124150-03bf7bfc1b66 h1:hmtb2Q3lHxq+SXqG+Gn43pKhTRYx+sw5j1LpgBfXN1o=
git.defalsify.org/vise.git v0.3.2-0.20250528124150-03bf7bfc1b66/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= git.defalsify.org/vise.git v0.3.2-0.20250528124150-03bf7bfc1b66/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
git.grassecon.net/grassrootseconomics/common v0.9.0-beta.1.0.20251127132814-8ceadabbc215 h1:cxWmd3WG3iVEqP6qG8ZeQRa7Ujno3rSKz3YXjZnmTEY= 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.20251127132814-8ceadabbc215/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.20251128071248-bfdeef125576 h1:Ov4zENfEnzuU4ZpsNGbFjog9NUM0h1A7RYwWkmHRJWo= git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250623063234-c1797e7a32b5 h1:VnRe01kHkZUBK/QjE7iV6gElSqSwQnAkWV3yCHtuYrI=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251128071248-bfdeef125576/go.mod h1:h/y/lJNJAVTcIzAxCMXXw8Dh2aoLxBFZ6F1nTB8C0nU= git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250623063234-c1797e7a32b5/go.mod h1:H97hR+VOnZvR5BiGVb0ScCPwH/IoKBOlKM+yrQNVpq0=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251202085112-45469d4ba326 h1:qH4QulgncvAD7b/YeHGPxcDJTBIychPeoZJACefYryI= git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250623070026-d945964b0b46 h1:0+XkSRe7XSHa9WHXKpGPuC0myDszjchr4syH006lQ28=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251202085112-45469d4ba326/go.mod h1:h/y/lJNJAVTcIzAxCMXXw8Dh2aoLxBFZ6F1nTB8C0nU= git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250623070026-d945964b0b46/go.mod h1:y/vsN8UO0wSxZk3gv0y5df4RPKMJI6TIxjVcVCPF8T8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20260207150752-71aa5ce7b537 h1:2AoOHiRTN3SXX4qnc2wOaF2ktVXLlFAa3X/n9DLu8/s= git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250623075057-7b42d509e6d4 h1:W+8CC7x5eCPylkGy2TEoOpfJuiIlqzEzyYTzCLlY/u8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20260207150752-71aa5ce7b537/go.mod h1:h/y/lJNJAVTcIzAxCMXXw8Dh2aoLxBFZ6F1nTB8C0nU= git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250623075057-7b42d509e6d4/go.mod h1:y/vsN8UO0wSxZk3gv0y5df4RPKMJI6TIxjVcVCPF8T8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250624074830-5aa032400c12 h1:vD8biQmN36eouuE+TdxgXQjKisRf5cTGu/tMPv3afs0=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250624074830-5aa032400c12/go.mod h1:y/vsN8UO0wSxZk3gv0y5df4RPKMJI6TIxjVcVCPF8T8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250624090744-339ba854c997 h1:8bCKyYoV4YiVBvCZlRclc3aQlBYpWhgtM35mvniDFD8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250624090744-339ba854c997/go.mod h1:y/vsN8UO0wSxZk3gv0y5df4RPKMJI6TIxjVcVCPF8T8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250626065419-57ee409f9629 h1:ew3vCFrLS/7/8uULTTPCbsHzFntQ6X68SScnBEy3pl0=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250626065419-57ee409f9629/go.mod h1:y/vsN8UO0wSxZk3gv0y5df4RPKMJI6TIxjVcVCPF8T8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250630213135-50ee455e7069 h1:re+hdr5NAC6JqhyvjMCkgX17fFi0u3Mawc6RBnBJW8I=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250630213135-50ee455e7069/go.mod h1:y/vsN8UO0wSxZk3gv0y5df4RPKMJI6TIxjVcVCPF8T8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250630213606-12940bb5f284 h1:2zMU9jPd6xEO6oY9oxr84sdT9G3d09eyAkjVBAz9eco=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250630213606-12940bb5f284/go.mod h1:y/vsN8UO0wSxZk3gv0y5df4RPKMJI6TIxjVcVCPF8T8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250630214912-814bef2b209a h1:KuhJ/WY4RCGmrXUA680ciaponM4vM5zBOJfnCpUo2fc=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250630214912-814bef2b209a/go.mod h1:y/vsN8UO0wSxZk3gv0y5df4RPKMJI6TIxjVcVCPF8T8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251021120522-6f7802b58cf5 h1:bQglHVxMilciZ9M2PGuLgA+Wkvqo8OqQh6TFYwjtuSE=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251021120522-6f7802b58cf5/go.mod h1:y/vsN8UO0wSxZk3gv0y5df4RPKMJI6TIxjVcVCPF8T8=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251022084613-532547899f63 h1:yznaUXeAy+qiZb2nCxosYXE5HyCPpynIoplEuYV/zQM=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251022084613-532547899f63/go.mod h1:y/vsN8UO0wSxZk3gv0y5df4RPKMJI6TIxjVcVCPF8T8=
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/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251119083800-2aa1d4cc79d7 h1:uups37roJCTtR/BrJa0WoMrxt3rzgV+Qrj+TxYyJoAo= github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251119083800-2aa1d4cc79d7/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0= github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI=
github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU= github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI=
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=
github.com/alecthomas/participle/v2 v2.0.0/go.mod h1:rAKZdJldHu8084ojcWevWAL8KmEU+AT+Olodb+WoN2Y= github.com/alecthomas/participle/v2 v2.0.0/go.mod h1:rAKZdJldHu8084ojcWevWAL8KmEU+AT+Olodb+WoN2Y=
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= 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 v1.0.0 h1:qCp1hUzZT8C8yHcdDo4sZQg2jHEaX6LF5H/dF9ba0qs= github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c h1:H9Nm+I7Cg/YVPpEV1RzU3Wq2pjamPc/UtHDgItcb7lE=
github.com/barbashov/iso639-3 v1.0.0/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.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
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/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/consensys/gnark-crypto v0.19.2 h1:qrEAIXq3T4egxqiliFFoNrepkIWVEeIYwt3UL0fvS80= github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I=
github.com/consensys/gnark-crypto v0.19.2/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0= github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8=
github.com/crate-crypto/go-eth-kzg v1.4.0 h1:WzDGjHk4gFg6YzV0rJOAsTK4z3Qkz5jd4RE3DAvPFkg= github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4=
github.com/crate-crypto/go-eth-kzg v1.4.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI= github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M=
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
github.com/cockroachdb/pebble v1.1.2 h1:CUh2IPtR4swHlEj48Rhfzw6l/d0qA31fItcIszQVIsA=
github.com/cockroachdb/pebble v1.1.2/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU=
github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
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.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ= github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA=
github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s= github.com/ethereum/go-ethereum v1.14.9 h1:J7iwXDrtUyE9FUjUYbd4c9tyzwMh6dTJsKzo9i6SrwA=
github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs= github.com/ethereum/go-ethereum v1.14.9/go.mod h1:QeW+MtTpRdBEm2pUFoonByee8zfHv7kGp0wK0odvU1I=
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk= github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A=
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8= github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
github.com/ethereum/go-ethereum v1.16.7 h1:qeM4TvbrWK0UC0tgkZ7NiRsmBGwsjqc64BHo20U59UQ= github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
github.com/ethereum/go-ethereum v1.16.7/go.mod h1:Fs6QebQbavneQTYcA39PEKv2+zIjX7rPUZ14DER46wk= github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg=
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/go-ole/go-ole v1.2.6/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 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
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/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/grassrootseconomics/eth-custodial v1.12.0-rc h1:INkXMfNUISkQ1bgnw3LwfFJn/eCdBawFU8Hx+S7mM/c= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grassrootseconomics/eth-custodial v1.12.0-rc/go.mod h1:s/vacSWFiVO7FDv76EID8sQvdT1vq+3GcWZUgU6N618= github.com/grassrootseconomics/eth-custodial v1.3.0-beta h1:twrMBhl89GqDUL9PlkzQxMP/6OST1BByrNDj+rqXDmU=
github.com/grassrootseconomics/ethutils v1.6.0 h1:Zx7Nqi9ACxkpsHLmWWj8AHGCGeBmjP8Qf79+YMTGLTY= github.com/grassrootseconomics/eth-custodial v1.3.0-beta/go.mod h1:7uhRcdnJplX4t6GKCEFkbeDhhjlcaGJeJqevbcvGLZo=
github.com/grassrootseconomics/ethutils v1.6.0/go.mod h1:7RCNwDj1wfgWt8CV4p7cQuZWPPI5fjwl/piq7NthaWg= github.com/grassrootseconomics/ethutils v1.3.1 h1:LlQO90HjJkl7ejC8fv6jP7RJUrAm1j4VMMCYfsoIrhU=
github.com/grassrootseconomics/ussd-data-service v1.10.1-beta h1:ZrRn7gv4MVSg67hmSdiii/Y86PUIvhxtqc2luAjlA7o= github.com/grassrootseconomics/ethutils v1.3.1/go.mod h1:Wuv1VEZrkLIXqTSEYI3Nh9HG/ZHOUQ+U+xvWJ8QtjgQ=
github.com/grassrootseconomics/ussd-data-service v1.10.1-beta/go.mod h1:Uigrnnwj0vaTz2az3VwHmAG7xpJgQWYTcLudLh6Zc1I= github.com/grassrootseconomics/ussd-data-service v1.5.0-beta h1:BSSQL/yPEvTVku9ja/ENZyZdwZkEaiTzzOUfg72bTy4=
github.com/grassrootseconomics/ussd-data-service v1.5.0-beta/go.mod h1:9sGnorpKaK76FmOGXoh/xv7x5siSFNYdXxQo9BKW4DI=
github.com/grassrootseconomics/ussd-data-service v1.6.0-beta h1:pY6zns6ifXyClRxP+JJaWrged3oRE7tTS2xaftF9clA=
github.com/grassrootseconomics/ussd-data-service v1.6.0-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/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= github.com/holiman/uint256 v1.3.1 h1:JfTzmih28bittyHM8z360dCjIA9dbPIBlcTI6lmctQs=
github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= 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=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk= github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs=
github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
github.com/lmittmann/w3 v0.20.5 h1:JN9eUaVifhnnEP5cdYpIvhvC6x2hUn9+LcWyPKLl8DI= github.com/lmittmann/w3 v0.17.1 h1:zdXIimmNmYfqOFur+Jqc9Yqwtq6jwnsQufbTOnSAtW4=
github.com/lmittmann/w3 v0.20.5/go.mod h1:N/QfdxE9A8PpdX37AVJ4NjCdG9L5ZSuhqrru01ieRpM= github.com/lmittmann/w3 v0.17.1/go.mod h1:WVUGMbL83WYBu4Sge3SVlW3qIG4VaHe+S8+UUnwz9Eg=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
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/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 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/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pashagolub/pgxmock/v4 v4.7.0 h1:de2ORuFYyjwOQR7NBm57+321RnZxpYiuUjsmqRiqgh8= github.com/pashagolub/pgxmock/v4 v4.3.0 h1:DqT7fk0OCK6H0GvqtcMsLpv8cIwWqdxWgfZNLeHCb/s=
github.com/pashagolub/pgxmock/v4 v4.7.0/go.mod h1:9L57pC193h2aKRHVyiiE817avasIPZnPwPlw3JczWvM= 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=
github.com/peteole/testdata-loader v0.3.0/go.mod h1:Mt0ZbRtb56u8SLJpNP+BnQbENljMorYBpqlvt3cS83U= github.com/peteole/testdata-loader v0.3.0/go.mod h1:Mt0ZbRtb56u8SLJpNP+BnQbENljMorYBpqlvt3cS83U=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
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/prometheus/client_golang v1.12.0 h1:C+UIj/QWtmqY13Arb8kwMt5j34/0Z2iKamrJ+ryC0Gg=
github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a h1:CmF68hwI0XsOQ5UwlBopMi2Ow4Pbg32akc4KIVCOm+Y=
github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
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.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= 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.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
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.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE= github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4=
github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= 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=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= 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.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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= 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=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
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=
@ -174,3 +232,5 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
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

@ -3,13 +3,11 @@ package application
import ( import (
"context" "context"
"fmt" "fmt"
"strconv"
"git.defalsify.org/vise.git/db" "git.defalsify.org/vise.git/db"
"git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/resource"
"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"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
"gopkg.in/leonelquinteros/gotext.v1" "gopkg.in/leonelquinteros/gotext.v1"
) )
@ -54,7 +52,6 @@ func (h *MenuHandlers) CheckBalance(ctx context.Context, sym string, input []byt
return res, err return res, err
} }
} }
content, err = loadUserContent(ctx, string(activeSym), string(activeBal), string(accAlias)) content, err = loadUserContent(ctx, string(activeSym), string(activeBal), string(accAlias))
if err != nil { if err != nil {
return res, err return res, err
@ -65,7 +62,7 @@ func (h *MenuHandlers) CheckBalance(ctx context.Context, sym string, input []byt
} }
// loadUserContent loads the main user content in the main menu: the alias, balance and active symbol associated with active voucher // loadUserContent loads the main user content in the main menu: the alias, balance and active symbol associated with active voucher
func loadUserContent(ctx context.Context, activeSym, balance, alias string) (string, error) { func loadUserContent(ctx context.Context, activeSym string, balance string, alias string) (string, error) {
var content string var content string
code := codeFromCtx(ctx) code := codeFromCtx(ctx)
@ -78,9 +75,8 @@ func loadUserContent(ctx context.Context, activeSym, balance, alias string) (str
formattedAmount = "0.00" formattedAmount = "0.00"
} }
// format the final outputs // format the final output
balStr := fmt.Sprintf("%s %s", formattedAmount, activeSym) balStr := fmt.Sprintf("%s %s", formattedAmount, activeSym)
if alias != "" { if alias != "" {
content = l.Get("%s\nBalance: %s\n", alias, balStr) content = l.Get("%s\nBalance: %s\n", alias, balStr)
} else { } else {
@ -102,144 +98,3 @@ func (h *MenuHandlers) FetchCommunityBalance(ctx context.Context, sym string, in
res.Content = l.Get("Community Balance: 0.00") res.Content = l.Get("Community Balance: 0.00")
return res, nil return res, nil
} }
// CalculateCreditAndDebt calls the API to get the credit and debt
// uses the pretium rates to convert the value to Ksh
func (h *MenuHandlers) CalculateCreditAndDebt(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_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
// Fetch session data
_, activeBal, activeSym, activeAddress, publicKey, activeDecimal, err := h.getSessionData(ctx, sessionId)
if err != nil {
res.Content = l.Get("Credit: %s KSH\nDebt: %s %s\n", "0", "0", string(activeSym))
return res, nil
}
res.FlagReset = append(res.FlagReset, flag_api_call_error)
// Resolve active pool
activePoolAddress, _, err := h.resolveActivePoolDetails(ctx, sessionId)
if err != nil {
return res, err
}
// Fetch swappable vouchers (pool view)
swappableVouchers, err := h.accountService.GetPoolSwappableFromVouchers(ctx, string(activePoolAddress), string(publicKey))
if err != nil {
logg.ErrorCtxf(ctx, "failed on GetPoolSwappableFromVouchers", "error", err)
res.Content = l.Get("Credit: %s KSH\nDebt: %s %s\n", "0", "0", string(activeSym))
return res, nil
}
if len(swappableVouchers) == 0 {
res.Content = l.Get("Credit: %s KSH\nDebt: %s %s\n", "0", "0", string(activeSym))
return res, nil
}
// Fetch ALL wallet vouchers (voucher holdings view)
allVouchers, err := h.accountService.FetchVouchers(ctx, string(publicKey))
if err != nil {
logg.ErrorCtxf(ctx, "failed on FetchVouchers", "error", err)
return res, nil
}
// CREDIT calculation
// Rule:
// 1. Swap quote of active voucher → first stable in pool from GetPoolSwappableFromVouchers
// 2. PLUS all stable balances from FetchVouchers
scaledCredit := "0"
// 1. Find first stable voucher in POOL (for swap target)
var firstPoolStable *dataserviceapi.TokenHoldings
for i := range swappableVouchers {
if isStableVoucher(swappableVouchers[i].TokenAddress) {
firstPoolStable = &swappableVouchers[i]
break
}
}
// 2. If pool has a stable, get swap quote
if firstPoolStable != nil {
finalAmountStr, err := store.ParseAndScaleAmount(
string(activeBal),
string(activeDecimal),
)
if err != nil {
return res, err
}
// swap active -> FIRST stable from pool list
r, err := h.accountService.GetPoolSwapQuote(ctx, finalAmountStr, string(publicKey), string(activeAddress), string(activePoolAddress), firstPoolStable.TokenAddress)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = l.Get("Your request failed. Please try again later.")
logg.ErrorCtxf(ctx, "failed on poolSwap", "error", err)
return res, nil
}
// scale using REAL stable decimals
finalQuote := store.ScaleDownBalance(r.OutValue, firstPoolStable.TokenDecimals)
scaledCredit = store.AddDecimalStrings(scaledCredit, finalQuote)
}
// 3. Add ALL wallet stable balances (from FetchVouchers)
for _, v := range allVouchers {
if isStableVoucher(v.TokenAddress) {
scaled := store.ScaleDownBalance(v.Balance, v.TokenDecimals)
scaledCredit = store.AddDecimalStrings(scaledCredit, scaled)
}
}
// DEBT calculation
// Rule:
// - Default = 0
// - If active is stable → remain 0
// - If active is non-stable and exists in pool → use pool balance
scaledDebt := "0"
if !isStableVoucher(string(activeAddress)) {
for _, v := range swappableVouchers {
if v.TokenSymbol == string(activeSym) {
scaledDebt = store.ScaleDownBalance(v.Balance, v.TokenDecimals)
break
}
}
}
formattedDebt, _ := store.TruncateDecimalString(scaledDebt, 2)
// Fetch MPESA rates
rates, err := h.accountService.GetMpesaOnrampRates(ctx)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = l.Get("Your request failed. Please try again later.")
logg.ErrorCtxf(ctx, "failed on GetMpesaOnrampRates", "error", err)
return res, nil
}
creditFloat, _ := strconv.ParseFloat(scaledCredit, 64)
creditKsh := fmt.Sprintf("%f", creditFloat*rates.Buy)
kshFormattedCredit, _ := store.TruncateDecimalString(creditKsh, 0)
res.Content = l.Get(
"Credit: %s KSH\nDebt: %s %s\n",
kshFormattedCredit,
formattedDebt,
string(activeSym),
)
return res, nil
}

View File

@ -52,7 +52,7 @@ func TestCheckBalance(t *testing.T) {
alias: "user72", alias: "user72",
activeSym: "SRF", activeSym: "SRF",
activeBal: "10.967", activeBal: "10.967",
expectedResult: resource.Result{Content: "user72\nBalance: 10.96 SRF\n"}, expectedResult: resource.Result{Content: "user72 balance: 10.96 SRF\n"},
expectError: false, expectError: false,
}, },
} }

View File

@ -1,678 +0,0 @@
package application
import (
"context"
"fmt"
"strconv"
"time"
"git.defalsify.org/vise.git/resource"
"git.grassecon.net/grassrootseconomics/common/hex"
"git.grassecon.net/grassrootseconomics/common/phone"
"git.grassecon.net/grassrootseconomics/sarafu-vise/config"
"git.grassecon.net/grassrootseconomics/sarafu-vise/store"
storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
"gopkg.in/leonelquinteros/gotext.v1"
)
// GetMpesaMaxLimit returns the max FROM token
// check if max/tokenDecimals > 0.1 for UX purposes and to prevent swapping of dust values
func (h *MenuHandlers) GetMpesaMaxLimit(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_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
flag_low_swap_amount, _ := h.flagManager.GetFlag("flag_low_swap_amount")
flag_incorrect_pool, _ := h.flagManager.GetFlag("flag_incorrect_pool")
flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher")
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
inputStr := string(input)
if inputStr == "0" || inputStr == "99" || inputStr == "88" || inputStr == "98" {
res.FlagReset = append(res.FlagReset, flag_low_swap_amount, flag_api_call_error, flag_incorrect_voucher, flag_incorrect_pool)
return res, nil
}
userStore := h.userdataStore
metadata, err := store.GetOrderedVoucherData(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 transaction voucher data (from token)
if err := store.StoreTransactionVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil {
logg.ErrorCtxf(ctx, "failed on StoreTransactionVoucher", "error", err)
return res, err
}
// Fetch session data
_, _, _, _, publicKey, _, err := h.getSessionData(ctx, sessionId)
if err != nil {
return res, err
}
// call the mpesa rates API to get the rates
rates, err := h.accountService.GetMpesaOnrampRates(ctx)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = l.Get("Your request failed. Please try again later.")
logg.ErrorCtxf(ctx, "failed on GetMpesaOnrampRates", "error", err)
return res, nil
}
txType := "swap"
mpesaAddress := config.DefaultMpesaAddress()
// Normalize the mpesa address to fetch the phone number
publicKeyNormalized, err := hex.NormalizeHex(mpesaAddress)
if err != nil {
logg.ErrorCtxf(ctx, "Failed to normalize alias address", "address", mpesaAddress, "error", err)
return res, err
}
// get the recipient's phone number from the address
recipientPhoneNumber, err := userStore.ReadEntry(ctx, publicKeyNormalized, storedb.DATA_PUBLIC_KEY_REVERSE)
if err != nil || len(recipientPhoneNumber) == 0 {
logg.WarnCtxf(ctx, "Alias address not registered, switching to normal transaction", "address", mpesaAddress)
recipientPhoneNumber = nil
}
// store it for future reference (TODO)
if err := userStore.WriteEntry(ctx, sessionId, storedb.DATA_RECIPIENT_PHONE_NUMBER, recipientPhoneNumber); err != nil {
logg.ErrorCtxf(ctx, "Failed to write recipient phone number", "value", string(recipientPhoneNumber), "error", err)
return res, err
}
// fetch data for verification (to_voucher data)
recipientActiveSym, recipientActiveAddress, recipientActiveDecimal, err := h.getRecipientData(ctx, string(recipientPhoneNumber))
if err != nil {
return res, err
}
// Fetch min withdrawal amount from config/env
minksh := fmt.Sprintf("%f", config.MinMpesaWithdrawAmount())
minKshFormatted, _ := store.TruncateDecimalString(minksh, 0)
// If SAT is the same as RAT, return early with KSH format
if string(metadata.TokenAddress) == string(recipientActiveAddress) {
txType = "normal"
// Save the transaction type
if err := userStore.WriteEntry(ctx, sessionId, storedb.DATA_SEND_TRANSACTION_TYPE, []byte(txType)); err != nil {
logg.ErrorCtxf(ctx, "Failed to write transaction type", "type", txType, "error", err)
return res, err
}
activeFloat, _ := strconv.ParseFloat(string(metadata.Balance), 64)
ksh := fmt.Sprintf("%f", activeFloat*rates.Buy)
maxKshFormatted, _ := store.TruncateDecimalString(ksh, 0)
res.Content = l.Get(
"Enter the amount of Mpesa to withdraw: (Min: Ksh %s, Max %s Ksh)\n",
minKshFormatted,
maxKshFormatted,
)
res.FlagReset = append(res.FlagReset, flag_low_swap_amount, flag_api_call_error, flag_incorrect_voucher, flag_incorrect_pool)
return res, nil
}
// Resolve active pool address
activePoolAddress, _, err := h.resolveActivePoolDetails(ctx, sessionId)
if err != nil {
return res, err
}
// Check if selected token is swappable
canSwap, err := h.accountService.CheckTokenInPool(ctx, string(activePoolAddress), string(metadata.TokenAddress))
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
logg.ErrorCtxf(ctx, "failed on CheckTokenInPool", "error", err)
return res, nil
}
if !canSwap.CanSwapFrom { // pool issue (CATCH on .vis)
res.FlagSet = append(res.FlagSet, flag_incorrect_pool)
res.Content = "0"
return res, nil
}
// retrieve the max credit send amounts
_, maxRAT, err := h.calculateSendCreditLimits(ctx, activePoolAddress, []byte(metadata.TokenAddress), recipientActiveAddress, publicKey, []byte(metadata.TokenDecimals), recipientActiveDecimal)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = "0"
logg.ErrorCtxf(ctx, "failed on calculateSendCreditLimits", "error", err)
return res, nil
}
res.FlagReset = append(res.FlagReset, flag_api_call_error)
// Fallback if below minimum
maxFloat, _ := strconv.ParseFloat(maxRAT, 64)
if maxFloat < 0.1 {
formatted, _ := store.TruncateDecimalString(maxRAT, 2)
res.Content = formatted
res.FlagSet = append(res.FlagSet, flag_low_swap_amount)
return res, nil
}
res.FlagReset = append(res.FlagReset, flag_low_swap_amount)
// Save max RAT amount to be used in validating the user's input
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SWAP_MAX_AMOUNT, []byte(maxRAT))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write swap max amount (maxRAT)", "value", maxRAT, "error", err)
return res, err
}
// Save the transaction type
if err := userStore.WriteEntry(ctx, sessionId, storedb.DATA_SEND_TRANSACTION_TYPE, []byte(txType)); err != nil {
logg.ErrorCtxf(ctx, "Failed to write transaction type", "type", txType, "error", err)
return res, err
}
// save swap related data for the swap preview (the swap to)
swapMetadata := &dataserviceapi.TokenHoldings{
TokenAddress: string(recipientActiveAddress),
TokenSymbol: string(recipientActiveSym),
TokenDecimals: string(recipientActiveDecimal),
}
// Store the active swap_to data
if err := store.UpdateSwapToVoucherData(ctx, userStore, sessionId, swapMetadata); err != nil {
logg.ErrorCtxf(ctx, "failed on UpdateSwapToVoucherData", "error", err)
return res, err
}
maxKsh := maxFloat * rates.Buy
kshStr := fmt.Sprintf("%f", maxKsh)
maxKshFormatted, _ := store.TruncateDecimalString(kshStr, 0)
res.Content = l.Get(
"Enter the amount of Mpesa to withdraw: (Min: Ksh %s, Max %s Ksh)\n",
minKshFormatted,
maxKshFormatted,
)
res.FlagReset = append(res.FlagReset, flag_low_swap_amount, flag_api_call_error, flag_incorrect_voucher, flag_incorrect_pool)
return res, nil
}
// GetMpesaPreview displays the get mpesa preview and estimates
func (h *MenuHandlers) GetMpesaPreview(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")
}
// INPUT IN RAT Ksh
inputStr := string(input)
if inputStr == "0" || inputStr == "9" {
return res, nil
}
flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount")
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
userStore := h.userdataStore
// call the mpesa rates API to get the rates
rates, err := h.accountService.GetMpesaOnrampRates(ctx)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = l.Get("Your request failed. Please try again later.")
logg.ErrorCtxf(ctx, "failed on GetMpesaOnrampRates", "error", err)
return res, nil
}
// Input in Ksh
kshAmount, err := strconv.ParseFloat(inputStr, 64)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_invalid_amount)
res.Content = inputStr
return res, nil
}
min := config.MinMpesaWithdrawAmount()
if kshAmount < min {
// if the input is below the minimum
res.FlagSet = append(res.FlagSet, flag_invalid_amount)
res.Content = inputStr
return res, nil
}
// divide by the buy rate
inputAmount := kshAmount / rates.Buy
// Resolve active pool
activePoolAddress, _, err := h.resolveActivePoolDetails(ctx, sessionId)
if err != nil {
return res, err
}
transactionType, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_SEND_TRANSACTION_TYPE)
if err != nil {
return res, err
}
// get the selected voucher
mpesaWithdrawalVoucher, err := store.GetTransactionVoucherData(ctx, h.userdataStore, sessionId)
if err != nil {
logg.ErrorCtxf(ctx, "failed on GetTransactionVoucherData", "error", err)
return res, err
}
if string(transactionType) == "normal" {
// get the max based on the selected voucher balance
maxValue, err := strconv.ParseFloat(mpesaWithdrawalVoucher.Balance, 64)
if err != nil {
logg.ErrorCtxf(ctx, "Failed to convert the stored balance string to a float", "error", err)
return res, err
}
if inputAmount > maxValue {
res.FlagSet = append(res.FlagSet, flag_invalid_amount)
res.Content = inputStr
return res, nil
}
// Format the input amount to 2 decimal places
inputAmountStr := fmt.Sprintf("%f", inputAmount)
qouteInputAmount, _ := store.TruncateDecimalString(inputAmountStr, 2)
// store the inputAmountStr as the final amount (that will be sent)
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_AMOUNT, []byte(inputAmountStr))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write send amount value entry with", "key", storedb.DATA_AMOUNT, "value", inputAmountStr, "error", err)
return res, err
}
res.Content = l.Get(
"You are sending %s %s in order to receive ~ %s ksh",
qouteInputAmount, mpesaWithdrawalVoucher.TokenSymbol, inputStr,
)
return res, nil
}
swapToVoucher, err := store.ReadSwapToVoucher(ctx, h.userdataStore, sessionId)
if err != nil {
logg.ErrorCtxf(ctx, "failed on ReadSwapFromVoucher", "error", err)
return res, err
}
swapMaxAmount, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SWAP_MAX_AMOUNT)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read swapMaxAmount entry with", "key", storedb.DATA_ACTIVE_SWAP_MAX_AMOUNT, "error", err)
return res, err
}
// use the stored max RAT
maxRATValue, err := strconv.ParseFloat(string(swapMaxAmount), 64)
if err != nil {
logg.ErrorCtxf(ctx, "Failed to convert the swapMaxAmount to a float", "error", err)
return res, err
}
if inputAmount > maxRATValue {
res.FlagSet = append(res.FlagSet, flag_invalid_amount)
res.Content = inputStr
return res, nil
}
// Format the amount to 2 decimal places
formattedAmount, err := store.TruncateDecimalString(fmt.Sprintf("%f", inputAmount), 2)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_invalid_amount)
res.Content = inputStr
return res, nil
}
finalAmountStr, err := store.ParseAndScaleAmount(formattedAmount, swapToVoucher.TokenDecimals)
if err != nil {
return res, err
}
// call the credit send API to get the reverse quote
r, err := h.accountService.GetCreditSendReverseQuote(ctx, string(activePoolAddress), mpesaWithdrawalVoucher.TokenAddress, swapToVoucher.TokenAddress, finalAmountStr)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = l.Get("Your request failed. Please try again later.")
logg.ErrorCtxf(ctx, "failed GetCreditSendReverseQuote poolSwap", "error", err)
return res, nil
}
sendInputAmount := r.InputAmount // amount of SAT
sendOutputAmount := r.OutputAmount // amount of RAT
// store the sendOutputAmount as the final amount (that will be sent)
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_AMOUNT, []byte(sendOutputAmount))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write output amount value entry with", "key", storedb.DATA_AMOUNT, "value", sendOutputAmount, "error", err)
return res, err
}
// store the sendInputAmount as the swap amount
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SWAP_AMOUNT, []byte(sendInputAmount))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write swap amount entry with", "key", storedb.DATA_ACTIVE_SWAP_AMOUNT, "value", sendInputAmount, "error", err)
return res, err
}
// covert for display
quoteInputStr := store.ScaleDownBalance(sendInputAmount, mpesaWithdrawalVoucher.TokenDecimals)
// Format the quoteInputStr amount to 2 decimal places
qouteInputAmount, _ := store.TruncateDecimalString(quoteInputStr, 2)
res.Content = l.Get(
"You are sending %s %s in order to receive ~ %s ksh",
qouteInputAmount, mpesaWithdrawalVoucher.TokenSymbol, inputStr,
)
return res, nil
}
// InitiateGetMpesa calls the poolSwap, followed by the transfer and returns a confirmation based on the result.
func (h *MenuHandlers) InitiateGetMpesa(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")
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
userStore := h.userdataStore
mpesaAddress := config.DefaultMpesaAddress()
transactionType, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_SEND_TRANSACTION_TYPE)
if err != nil {
return res, err
}
// get the selected voucher
mpesaWithdrawalVoucher, err := store.GetTransactionVoucherData(ctx, h.userdataStore, sessionId)
if err != nil {
logg.ErrorCtxf(ctx, "failed on GetTransactionVoucherData", "error", err)
return res, err
}
if string(transactionType) == "normal" {
// Call TokenTransfer for the normal transaction
data, err := store.ReadTransactionData(ctx, h.userdataStore, sessionId)
if err != nil {
return res, err
}
finalAmountStr, err := store.ParseAndScaleAmount(data.Amount, mpesaWithdrawalVoucher.TokenDecimals)
if err != nil {
return res, err
}
tokenTransfer, err := h.accountService.TokenTransfer(ctx, finalAmountStr, data.PublicKey, mpesaAddress, mpesaWithdrawalVoucher.TokenAddress)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = l.Get("Your request failed. Please try again later.")
logg.ErrorCtxf(ctx, "failed on TokenTransfer", "error", err)
return res, nil
}
logg.InfoCtxf(ctx, "TokenTransfer normal", "trackingId", tokenTransfer.TrackingId)
res.Content = l.Get("Your request has been sent. Please await confirmation")
res.FlagReset = append(res.FlagReset, flag_account_authorized)
return res, nil
}
publicKey, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read publicKey entry", "key", storedb.DATA_PUBLIC_KEY, "error", err)
return res, err
}
swapToVoucher, err := store.ReadSwapToVoucher(ctx, h.userdataStore, sessionId)
if err != nil {
logg.ErrorCtxf(ctx, "failed on ReadSwapFromVoucher", "error", err)
return res, err
}
// Resolve active pool
activePoolAddress, _, err := h.resolveActivePoolDetails(ctx, 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
poolSwap, err := h.accountService.PoolSwap(ctx, swapAmountStr, string(publicKey), mpesaWithdrawalVoucher.TokenAddress, string(activePoolAddress), swapToVoucher.TokenAddress)
if err != nil {
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = l.Get("Your request failed. Please try again later.")
logg.ErrorCtxf(ctx, "failed on poolSwap", "error", err)
return res, nil
}
logg.InfoCtxf(ctx, "mpesa poolSwap before transfer", "swapTrackingId", poolSwap.TrackingId)
// TODO: remove this temporary time delay and replace with a swap and send endpoint
time.Sleep(1 * time.Second)
amount, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_AMOUNT)
if err != nil {
return res, err
}
// Initiate a send to mpesa after the swap
tokenTransfer, err := h.accountService.TokenTransfer(ctx, string(amount), string(publicKey), mpesaAddress, swapToVoucher.TokenAddress)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = l.Get("Your request failed. Please try again later.")
logg.ErrorCtxf(ctx, "failed on TokenTransfer after swap", "error", err)
return res, nil
}
logg.InfoCtxf(ctx, "final TokenTransfer after swap", "trackingId", tokenTransfer.TrackingId)
res.Content = l.Get("Your request has been sent. Please await confirmation")
res.FlagReset = append(res.FlagReset, flag_account_authorized)
return res, nil
}
// SendMpesaMinLimit returns the min amount from the config
func (h *MenuHandlers) SendMpesaMinLimit(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
inputStr := string(input)
if inputStr == "0" || inputStr == "9" {
return res, nil
}
// Fetch min amount from config/env
min := config.MinMpesaSendAmount()
// Convert to string
ksh := fmt.Sprintf("%f", min)
// Format (e.g., 100.0 -> 100)
kshFormatted, _ := store.TruncateDecimalString(ksh, 0)
res.Content = l.Get(
"Enter the amount of credit to deposit: (Minimum %s Ksh)\n",
kshFormatted,
)
return res, nil
}
// SendMpesaPreview displays the send mpesa preview and estimates
func (h *MenuHandlers) SendMpesaPreview(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")
}
// INPUT IN Ksh
inputStr := string(input)
if inputStr == "0" || inputStr == "9" {
return res, nil
}
flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount")
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
userStore := h.userdataStore
// call the mpesa rates API to get the rates
rates, err := h.accountService.GetMpesaOnrampRates(ctx)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = l.Get("Your request failed. Please try again later.")
logg.ErrorCtxf(ctx, "failed on GetMpesaOnrampRates", "error", err)
return res, nil
}
// Input in Ksh
kshAmount, err := strconv.ParseFloat(inputStr, 64)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_invalid_amount)
res.Content = inputStr
return res, nil
}
min := config.MinMpesaSendAmount()
max := config.MaxMpesaSendAmount()
if kshAmount > max || kshAmount < min {
res.FlagSet = append(res.FlagSet, flag_invalid_amount)
res.Content = inputStr
return res, nil
}
res.FlagReset = append(res.FlagReset, flag_invalid_amount)
// store the user's raw input amount
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_AMOUNT, []byte(inputStr))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write amount inputStr entry with", "key", storedb.DATA_AMOUNT, "value", inputStr, "error", err)
return res, err
}
estimateValue := kshAmount / rates.Sell
estimateStr := fmt.Sprintf("%f", estimateValue)
estimateFormatted, _ := store.TruncateDecimalString(estimateStr, 2)
defaultAsset := config.DefaultMpesaAsset()
res.Content = l.Get(
"You will get a prompt for your Mpesa PIN shortly to send %s ksh and receive ~ %s %s",
inputStr, estimateFormatted, defaultAsset,
)
return res, nil
}
// InitiateSendMpesa calls the trigger-onram API to initiate the purchase
func (h *MenuHandlers) InitiateSendMpesa(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")
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
userStore := h.userdataStore
publicKey, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read publicKey entry", "key", storedb.DATA_PUBLIC_KEY, "error", err)
return res, err
}
phoneNumber, err := phone.FormatToLocalPhoneNumber(sessionId)
if err != nil {
logg.ErrorCtxf(ctx, "failed on FormatToLocalPhoneNumber", "session-id", sessionId, "error", err)
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = l.Get("Your request failed. Please try again later.")
return res, nil
}
defaultAsset := config.DefaultMpesaAsset()
amount, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_AMOUNT)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read amount entry", "key", storedb.DATA_AMOUNT, "error", err)
return res, err
}
amountInt, err := strconv.Atoi(string(amount))
if err != nil {
logg.ErrorCtxf(ctx, "failed to convert amount to int", "amount", string(amount), "error", err)
return res, err
}
// Call the trigger onramp API
triggerOnramp, err := h.accountService.MpesaTriggerOnramp(ctx, string(publicKey), phoneNumber, defaultAsset, amountInt)
if err != nil {
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = l.Get("Your request failed. Please try again later.")
logg.ErrorCtxf(ctx, "failed on MpesaTriggerOnramp", "error", err)
return res, nil
}
logg.InfoCtxf(ctx, "MpesaTriggerOnramp", "transactionCode", triggerOnramp.TransactionCode)
res.Content = l.Get("Your request has been sent. Thank you for using Sarafu")
res.FlagReset = append(res.FlagReset, flag_account_authorized)
return res, nil
}

View File

@ -1,329 +0,0 @@
package application
import (
"context"
"fmt"
"strconv"
"strings"
"git.defalsify.org/vise.git/resource"
"git.grassecon.net/grassrootseconomics/sarafu-vise/config"
"git.grassecon.net/grassrootseconomics/sarafu-vise/store"
storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db"
"gopkg.in/leonelquinteros/gotext.v1"
)
// CalculateMaxPayDebt calculates the max debt removal based on the selected voucher
func (h *MenuHandlers) CalculateMaxPayDebt(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_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
flag_low_swap_amount, _ := h.flagManager.GetFlag("flag_low_swap_amount")
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
inputStr := string(input)
if inputStr == "0" || inputStr == "99" || inputStr == "88" || inputStr == "98" {
res.FlagReset = append(res.FlagReset, flag_low_swap_amount, flag_api_call_error)
return res, nil
}
userStore := h.userdataStore
// Fetch session data
_, _, activeSym, activeAddress, publicKey, activeDecimal, err := h.getSessionData(ctx, sessionId)
if err != nil {
return res, nil
}
// Resolve active pool
activePoolAddress, activePoolSymbol, err := h.resolveActivePoolDetails(ctx, sessionId)
if err != nil {
res.FlagReset = append(res.FlagReset, flag_low_swap_amount, flag_api_call_error)
return res, err
}
// get the voucher data based on the input
metadata, err := store.GetVoucherData(ctx, userStore, sessionId, inputStr)
if err != nil {
res.FlagReset = append(res.FlagReset, flag_low_swap_amount, flag_api_call_error)
return res, fmt.Errorf("failed to retrieve swap to voucher data: %v", err)
}
if metadata == nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
return res, nil
}
// Get the max swap limit with the selected voucher
r, err := h.accountService.GetSwapFromTokenMaxLimit(ctx, string(activePoolAddress), metadata.TokenAddress, string(activeAddress), string(publicKey))
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = "0"
logg.ErrorCtxf(ctx, "failed on GetSwapFromTokenMaxLimit", "error", err)
return res, nil
}
maxLimit := r.Max
metadata.Balance = maxLimit
// Store the active swap from data
if err := store.UpdateSwapFromVoucherData(ctx, userStore, sessionId, metadata); err != nil {
logg.ErrorCtxf(ctx, "failed on UpdateSwapFromVoucherData", "error", err)
return res, err
}
// Scale down the amount
maxAmountStr := store.ScaleDownBalance(maxLimit, metadata.TokenDecimals)
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, _ := store.TruncateDecimalString(string(maxAmountStr), 2)
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
}
// Do a pool quote to get the max AT that can be removed (gotten)
// if we swapped the max of the FT
// call the API to get the quote
qoute, err := h.accountService.GetPoolSwapQuote(ctx, maxLimit, string(publicKey), metadata.TokenAddress, string(activePoolAddress), string(activeAddress))
if err != nil {
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
res.FlagSet = append(res.FlagSet, flag_api_call_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(qoute.OutValue, string(activeDecimal))
// Format to 2 decimal places
quoteStr, _ := store.TruncateDecimalString(string(quoteAmountStr), 2)
res.Content = l.Get(
"You can remove a max of %s %s from '%s' pool\nEnter amount of %s:(Max: %s)",
quoteStr,
string(activeSym),
string(activePoolSymbol),
metadata.TokenSymbol,
maxStr,
)
res.FlagReset = append(res.FlagReset, flag_low_swap_amount, flag_api_call_error)
return res, nil
}
// ConfirmDebtRemoval displays the debt preview for a confirmation
func (h *MenuHandlers) ConfirmDebtRemoval(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
// Fetch session data
_, _, activeSym, activeAddress, publicKey, activeDecimal, err := h.getSessionData(ctx, sessionId)
if err != nil {
return res, nil
}
payDebtVoucher, err := store.ReadSwapFromVoucher(ctx, h.userdataStore, sessionId)
if err != nil {
logg.ErrorCtxf(ctx, "failed on ReadSwapFromVoucher", "error", err)
return res, err
}
// Resolve active pool
activePoolAddress, _, err := h.resolveActivePoolDetails(ctx, sessionId)
if err != nil {
return res, err
}
swapMaxAmount, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SWAP_MAX_AMOUNT)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read swapMaxAmount entry with", "key", storedb.DATA_ACTIVE_SWAP_MAX_AMOUNT, "error", err)
return res, err
}
maxValue, err := strconv.ParseFloat(string(swapMaxAmount), 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 || inputAmount < 0.1 {
res.FlagSet = append(res.FlagSet, flag_invalid_amount)
res.Content = inputStr
return res, nil
}
var finalAmountStr string
if inputStr == string(swapMaxAmount) {
finalAmountStr = string(payDebtVoucher.Balance)
} else {
finalAmountStr, err = store.ParseAndScaleAmount(inputStr, payDebtVoucher.TokenDecimals)
if err != nil {
return res, err
}
}
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SWAP_AMOUNT, []byte(finalAmountStr))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write swap amount entry with", "key", storedb.DATA_ACTIVE_SWAP_AMOUNT, "value", finalAmountStr, "error", err)
return res, err
}
// call the API to get the quote
r, err := h.accountService.GetPoolSwapQuote(ctx, finalAmountStr, string(publicKey), payDebtVoucher.TokenAddress, string(activePoolAddress), string(activeAddress))
if err != nil {
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
res.FlagSet = append(res.FlagSet, flag_api_call_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 (for the AT)
quoteAmountStr := store.ScaleDownBalance(r.OutValue, string(activeDecimal))
// Format to 2 decimal places
qouteStr, _ := store.TruncateDecimalString(string(quoteAmountStr), 2)
// store the quote in the temporary value key
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(qouteStr))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write swap max amount entry with", "key", storedb.DATA_TEMPORARY_VALUE, "value", qouteStr, "error", err)
return res, err
}
res.Content = l.Get(
"Please confirm that you will use %s %s to remove your debt of %s %s\nEnter your PIN:",
inputStr, payDebtVoucher.TokenSymbol, qouteStr, string(activeSym),
)
return res, nil
}
// InitiatePayDebt calls the poolSwap to swap the token for the active voucher.
func (h *MenuHandlers) InitiatePayDebt(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
// Fetch session data
_, _, activeSym, activeAddress, publicKey, _, err := h.getSessionData(ctx, sessionId)
if err != nil {
return res, nil
}
// Resolve active pool
activePoolAddress, activePoolSymbol, err := h.resolveActivePoolDetails(ctx, sessionId)
if err != nil {
return res, err
}
payDebtVoucher, err := store.ReadSwapFromVoucher(ctx, h.userdataStore, sessionId)
if err != nil {
logg.ErrorCtxf(ctx, "failed on ReadSwapFromVoucher", "error", err)
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
}
debtQuotedAmount, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read debtQuotedAmount entry with", "key", storedb.DATA_TEMPORARY_VALUE, "error", err)
return res, err
}
swapAmountStr := string(swapAmount)
// Call the poolSwap API
r, err := h.accountService.PoolSwap(ctx, swapAmountStr, string(publicKey), payDebtVoucher.TokenAddress, string(activePoolAddress), string(activeAddress))
if err != nil {
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
res.FlagSet = append(res.FlagSet, flag_api_call_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 debt of %s %s has been removed from %s.",
string(debtQuotedAmount),
string(activeSym),
activePoolSymbol,
)
res.FlagReset = append(res.FlagReset, flag_account_authorized)
return res, nil
}
func isStableVoucher(tokenAddress string) bool {
addr := strings.TrimSpace(tokenAddress)
for _, stable := range config.StableVoucherAddresses() {
if addr == stable {
return true
}
}
return false
}

View File

@ -1,231 +0,0 @@
package application
import (
"context"
"fmt"
"strconv"
"strings"
"git.defalsify.org/vise.git/resource"
"git.grassecon.net/grassrootseconomics/sarafu-vise/store"
storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db"
"gopkg.in/leonelquinteros/gotext.v1"
)
// GetOrderedVouchers returns a list of ordered vouchers with stables at the top
func (h *MenuHandlers) GetOrderedVouchers(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")
userStore := h.userdataStore
// Read ordered vouchers from the store
voucherData, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ORDERED_VOUCHER_SYMBOLS)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read stable voucherData entires with", "key", storedb.DATA_ORDERED_VOUCHER_SYMBOLS, "error", err)
return res, err
}
if len(voucherData) == 0 {
return res, nil
}
voucherBalances, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ORDERED_VOUCHER_BALANCES)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read stable voucherData entires with", "key", storedb.DATA_ORDERED_VOUCHER_BALANCES, "error", err)
return res, err
}
formattedVoucherList := store.FormatVoucherList(ctx, string(voucherData), string(voucherBalances))
finalOutput := strings.Join(formattedVoucherList, "\n")
res.Content = l.Get("Select number or symbol from your vouchers:\n%s", finalOutput)
return res, nil
}
// PoolDepositMaxAmount returns the balance of the selected voucher
func (h *MenuHandlers) PoolDepositMaxAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher")
res.FlagReset = append(res.FlagReset, flag_incorrect_voucher)
inputStr := string(input)
if inputStr == "0" || inputStr == "99" || inputStr == "88" || inputStr == "98" {
return res, nil
}
userStore := h.userdataStore
metadata, err := store.GetOrderedVoucherData(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 pool deposit voucher data
if err := store.StoreTransactionVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil {
logg.ErrorCtxf(ctx, "failed on StoreTransactionVoucher", "error", err)
return res, err
}
// Format the balance amount to 2 decimal places
formattedBalance, _ := store.TruncateDecimalString(string(metadata.Balance), 2)
res.Content = l.Get("Maximum amount: %s %s\nEnter amount:", formattedBalance, metadata.TokenSymbol)
return res, nil
}
// ConfirmPoolDeposit displays the pool deposit preview for a PIN confirmation
func (h *MenuHandlers) ConfirmPoolDeposit(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")
res.FlagReset = append(res.FlagReset, flag_invalid_amount)
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
userStore := h.userdataStore
poolDepositVoucher, err := store.GetTransactionVoucherData(ctx, h.userdataStore, sessionId)
if err != nil {
logg.ErrorCtxf(ctx, "failed on GetTransactionVoucherData", "error", err)
return res, err
}
maxValue, err := strconv.ParseFloat(poolDepositVoucher.Balance, 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 || inputAmount < 0.1 {
res.FlagSet = append(res.FlagSet, flag_invalid_amount)
res.Content = inputStr
return res, nil
}
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_AMOUNT, []byte(inputStr))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write pool deposit amount entry with", "key", storedb.DATA_AMOUNT, "value", inputStr, "error", err)
return res, err
}
// Resolve active pool
_, activePoolSymbol, err := h.resolveActivePoolDetails(ctx, sessionId)
if err != nil {
return res, err
}
res.Content = l.Get(
"You will deposit %s %s into %s\n",
inputStr, poolDepositVoucher.TokenSymbol, activePoolSymbol,
)
return res, nil
}
// InitiatePoolDeposit calls the pool deposit API
func (h *MenuHandlers) InitiatePoolDeposit(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
// Resolve active pool
activePoolAddress, activePoolSymbol, err := h.resolveActivePoolDetails(ctx, sessionId)
if err != nil {
logg.ErrorCtxf(ctx, "failed on resolveActivePoolDetails", "error", err)
return res, err
}
poolDepositVoucher, err := store.GetTransactionVoucherData(ctx, h.userdataStore, sessionId)
if err != nil {
logg.ErrorCtxf(ctx, "failed on GetTransactionVoucherData", "error", err)
return res, err
}
publicKey, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read publicKey entry", "key", storedb.DATA_PUBLIC_KEY, "error", err)
return res, err
}
amount, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_AMOUNT)
if err != nil {
logg.ErrorCtxf(ctx, "failed to read amount entry", "key", storedb.DATA_AMOUNT, "error", err)
return res, err
}
finalAmountStr, err := store.ParseAndScaleAmount(string(amount), poolDepositVoucher.TokenDecimals)
if err != nil {
logg.ErrorCtxf(ctx, "failed on ParseAndScaleAmount", "error", err)
return res, err
}
// Call pool deposit API
r, err := h.accountService.PoolDeposit(ctx, finalAmountStr, string(publicKey), string(activePoolAddress), poolDepositVoucher.TokenAddress)
if err != nil {
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = l.Get("Your request failed. Please try again later.")
logg.ErrorCtxf(ctx, "failed on pool deposit", "error", err)
return res, nil
}
trackingId := r.TrackingId
logg.InfoCtxf(ctx, "Pool deposit", "trackingId", trackingId)
res.Content = l.Get(
"Your request has been sent. You will receive an SMS when %s %s has been deposited into %s.",
string(amount),
poolDepositVoucher.TokenSymbol,
activePoolSymbol,
)
res.FlagReset = append(res.FlagReset, flag_account_authorized)
return res, nil
}

View File

@ -3,7 +3,6 @@ package application
import ( import (
"context" "context"
"fmt" "fmt"
"strings"
"git.defalsify.org/vise.git/db" "git.defalsify.org/vise.git/db"
"git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/resource"
@ -23,12 +22,12 @@ func (h *MenuHandlers) GetPools(ctx context.Context, sym string, input []byte) (
} }
userStore := h.userdataStore userStore := h.userdataStore
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error") flag_api_error, _ := h.flagManager.GetFlag("flag_api_error")
// call the api to get a list of top 5 pools sorted by swaps // call the api to get a list of top 5 pools sorted by swaps
topPools, err := h.accountService.FetchTopPools(ctx) topPools, err := h.accountService.FetchTopPools(ctx)
if err != nil { if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error) res.FlagSet = append(res.FlagSet, flag_api_error)
logg.ErrorCtxf(ctx, "failed on FetchTransactions", "error", err) logg.ErrorCtxf(ctx, "failed on FetchTransactions", "error", err)
return res, err return res, err
} }
@ -105,7 +104,6 @@ func (h *MenuHandlers) GetDefaultPool(ctx context.Context, sym string, input []b
// ViewPool retrieves the pool details from the user store // ViewPool retrieves the pool details from the user store
// and displays it to the user for them to select it. // and displays it to the user for them to select it.
// if the data does not exist, it calls the API to get the pool details
func (h *MenuHandlers) ViewPool(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *MenuHandlers) ViewPool(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
sessionId, ok := ctx.Value("SessionId").(string) sessionId, ok := ctx.Value("SessionId").(string)
@ -131,15 +129,12 @@ func (h *MenuHandlers) ViewPool(ctx context.Context, sym string, input []byte) (
} }
if poolData == nil { if poolData == nil {
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error") flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
// convert to uppercase before the call
poolSymbol := strings.ToUpper(inputStr)
// no match found. Call the API using the inputStr as the symbol // no match found. Call the API using the inputStr as the symbol
poolResp, err := h.accountService.RetrievePoolDetails(ctx, poolSymbol) poolResp, err := h.accountService.RetrievePoolDetails(ctx, inputStr)
if err != nil { if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error) res.FlagSet = append(res.FlagSet, flag_api_error)
return res, nil return res, nil
} }

View File

@ -41,7 +41,7 @@ func (h *MenuHandlers) LoadSwapToList(ctx context.Context, sym string, input []b
l.AddDomain("default") l.AddDomain("default")
flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher") flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher")
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error") flag_api_error, _ := h.flagManager.GetFlag("flag_api_error")
inputStr := string(input) inputStr := string(input)
if inputStr == "0" { if inputStr == "0" {
@ -88,7 +88,7 @@ func (h *MenuHandlers) LoadSwapToList(ctx context.Context, sym string, input []b
// call the api using the ActivePoolAddress and ActiveVoucherAddress to check if it is part of the pool // 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)) r, err := h.accountService.CheckTokenInPool(ctx, string(activePoolAddress), string(activeAddress))
if err != nil { if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error) res.FlagSet = append(res.FlagSet, flag_api_error)
logg.ErrorCtxf(ctx, "failed on CheckTokenInPool", "error", err) logg.ErrorCtxf(ctx, "failed on CheckTokenInPool", "error", err)
return res, err return res, err
} }
@ -110,7 +110,7 @@ func (h *MenuHandlers) LoadSwapToList(ctx context.Context, sym string, input []b
// call the api using the activePoolAddress to get a list of SwapToSymbolsData // call the api using the activePoolAddress to get a list of SwapToSymbolsData
swapToList, err := h.accountService.GetPoolSwappableVouchers(ctx, string(activePoolAddress)) swapToList, err := h.accountService.GetPoolSwappableVouchers(ctx, string(activePoolAddress))
if err != nil { if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error) res.FlagSet = append(res.FlagSet, flag_api_error)
logg.ErrorCtxf(ctx, "failed on FetchTransactions", "error", err) logg.ErrorCtxf(ctx, "failed on FetchTransactions", "error", err)
return res, err return res, err
} }
@ -165,7 +165,7 @@ func (h *MenuHandlers) SwapMaxLimit(ctx context.Context, sym string, input []byt
} }
flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher") flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher")
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error") flag_api_error, _ := h.flagManager.GetFlag("flag_api_error")
flag_low_swap_amount, _ := h.flagManager.GetFlag("flag_low_swap_amount") flag_low_swap_amount, _ := h.flagManager.GetFlag("flag_low_swap_amount")
res.FlagReset = append(res.FlagReset, flag_incorrect_voucher, flag_low_swap_amount) res.FlagReset = append(res.FlagReset, flag_incorrect_voucher, flag_low_swap_amount)
@ -175,10 +175,6 @@ func (h *MenuHandlers) SwapMaxLimit(ctx context.Context, sym string, input []byt
return res, nil return res, nil
} }
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
userStore := h.userdataStore userStore := h.userdataStore
metadata, err := store.GetSwapToVoucherData(ctx, userStore, sessionId, inputStr) metadata, err := store.GetSwapToVoucherData(ctx, userStore, sessionId, inputStr)
if err != nil { if err != nil {
@ -206,9 +202,9 @@ func (h *MenuHandlers) SwapMaxLimit(ctx context.Context, sym string, input []byt
logg.InfoCtxf(ctx, "Call GetSwapFromTokenMaxLimit with:", "ActivePoolAddress", swapData.ActivePoolAddress, "ActiveSwapFromAddress", swapData.ActiveSwapFromAddress, "ActiveSwapToAddress", swapData.ActiveSwapToAddress, "publicKey", swapData.PublicKey) logg.InfoCtxf(ctx, "Call GetSwapFromTokenMaxLimit with:", "ActivePoolAddress", swapData.ActivePoolAddress, "ActiveSwapFromAddress", swapData.ActiveSwapFromAddress, "ActiveSwapToAddress", swapData.ActiveSwapToAddress, "publicKey", swapData.PublicKey)
r, err := h.accountService.GetSwapFromTokenMaxLimit(ctx, swapData.ActivePoolAddress, swapData.ActiveSwapFromAddress, swapData.ActiveSwapToAddress, swapData.PublicKey) r, err := h.accountService.GetSwapFromTokenMaxLimit(ctx, swapData.ActivePoolAddress, swapData.ActiveSwapFromAddress, swapData.ActiveSwapToAddress, swapData.PublicKey)
if err != nil { if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error) res.FlagSet = append(res.FlagSet, flag_api_error)
logg.ErrorCtxf(ctx, "failed on GetSwapFromTokenMaxLimit", "error", err) logg.ErrorCtxf(ctx, "failed on GetSwapFromTokenMaxLimit", "error", err)
return res, nil return res, err
} }
// Scale down the amount // Scale down the amount
@ -224,7 +220,7 @@ func (h *MenuHandlers) SwapMaxLimit(ctx context.Context, sym string, input []byt
} }
// Format to 2 decimal places // Format to 2 decimal places
maxStr, _ := store.TruncateDecimalString(string(maxAmountStr), 2) maxStr := fmt.Sprintf("%.2f", maxAmountFloat)
if maxAmountFloat < 0.1 { if maxAmountFloat < 0.1 {
// return with low amount flag // return with low amount flag
@ -239,9 +235,9 @@ func (h *MenuHandlers) SwapMaxLimit(ctx context.Context, sym string, input []byt
return res, err return res, err
} }
res.Content = l.Get( res.Content = fmt.Sprintf(
"Maximum: %s %s\n\nEnter amount of %s to swap for %s:", "Maximum: %s\n\nEnter amount of %s to swap for %s:",
maxStr, swapData.ActiveSwapFromSym, swapData.ActiveSwapFromSym, swapData.ActiveSwapToSym, maxStr, swapData.ActiveSwapFromSym, swapData.ActiveSwapToSym,
) )
return res, nil return res, nil
@ -307,15 +303,15 @@ func (h *MenuHandlers) SwapPreview(ctx context.Context, sym string, input []byte
// store the user's input amount in the temporary value // store the user's input amount in the temporary value
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(inputStr)) err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(inputStr))
if err != nil { if err != nil {
logg.ErrorCtxf(ctx, "failed to write inputStr amount entry with", "key", storedb.DATA_TEMPORARY_VALUE, "value", inputStr, "error", err) logg.ErrorCtxf(ctx, "failed to write swap amount entry with", "key", storedb.DATA_ACTIVE_SWAP_AMOUNT, "value", finalAmountStr, "error", err)
return res, err return res, err
} }
// call the API to get the quote // call the API to get the quote
r, err := h.accountService.GetPoolSwapQuote(ctx, finalAmountStr, swapData.PublicKey, swapData.ActiveSwapFromAddress, swapData.ActivePoolAddress, swapData.ActiveSwapToAddress) r, err := h.accountService.GetPoolSwapQuote(ctx, finalAmountStr, swapData.PublicKey, swapData.ActiveSwapFromAddress, swapData.ActivePoolAddress, swapData.ActiveSwapToAddress)
if err != nil { if err != nil {
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error") flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
res.FlagSet = append(res.FlagSet, flag_api_call_error) res.FlagSet = append(res.FlagSet, flag_api_error)
res.Content = l.Get("Your request failed. Please try again later.") res.Content = l.Get("Your request failed. Please try again later.")
logg.ErrorCtxf(ctx, "failed on poolSwap", "error", err) logg.ErrorCtxf(ctx, "failed on poolSwap", "error", err)
return res, nil return res, nil
@ -323,12 +319,17 @@ func (h *MenuHandlers) SwapPreview(ctx context.Context, sym string, input []byte
// Scale down the quoted amount // Scale down the quoted amount
quoteAmountStr := store.ScaleDownBalance(r.OutValue, swapData.ActiveSwapToDecimal) 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 // Format to 2 decimal places
qouteStr, _ := store.TruncateDecimalString(string(quoteAmountStr), 2) qouteStr := fmt.Sprintf("%.2f", qouteAmount)
res.Content = l.Get( res.Content = fmt.Sprintf(
"You will swap %s %s for %s %s:", "You will swap:\n%s %s for %s %s:",
inputStr, swapData.ActiveSwapFromSym, qouteStr, swapData.ActiveSwapToSym, inputStr, swapData.ActiveSwapFromSym, qouteStr, swapData.ActiveSwapToSym,
) )
@ -368,8 +369,8 @@ func (h *MenuHandlers) InitiateSwap(ctx context.Context, sym string, input []byt
// Call the poolSwap API // Call the poolSwap API
r, err := h.accountService.PoolSwap(ctx, swapAmountStr, swapData.PublicKey, swapData.ActiveSwapFromAddress, swapData.ActivePoolAddress, swapData.ActiveSwapToAddress) r, err := h.accountService.PoolSwap(ctx, swapAmountStr, swapData.PublicKey, swapData.ActiveSwapFromAddress, swapData.ActivePoolAddress, swapData.ActiveSwapToAddress)
if err != nil { if err != nil {
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error") flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
res.FlagSet = append(res.FlagSet, flag_api_call_error) res.FlagSet = append(res.FlagSet, flag_api_error)
res.Content = l.Get("Your request failed. Please try again later.") res.Content = l.Get("Your request failed. Please try again later.")
logg.ErrorCtxf(ctx, "failed on poolSwap", "error", err) logg.ErrorCtxf(ctx, "failed on poolSwap", "error", err)
return res, nil return res, nil

File diff suppressed because it is too large Load Diff

View File

@ -277,7 +277,7 @@ func TestMaxAmount(t *testing.T) {
} }
} }
res, err := h.MaxAmount(ctx, "send_max_amount", []byte("")) res, err := h.MaxAmount(ctx, "max_amount", []byte(""))
if tt.expectedError { if tt.expectedError {
assert.Error(t, err) assert.Error(t, err)
@ -452,7 +452,7 @@ func TestGetAmount(t *testing.T) {
assert.Equal(t, formattedAmount, res.Content) assert.Equal(t, formattedAmount, res.Content)
} }
func TestInitiateNormalTransaction(t *testing.T) { func TestInitiateTransaction(t *testing.T) {
sessionId := "254712345678" sessionId := "254712345678"
ctx, store := InitializeTestStore(t) ctx, store := InitializeTestStore(t)
ctx = context.WithValue(ctx, "SessionId", sessionId) ctx = context.WithValue(ctx, "SessionId", sessionId)
@ -538,7 +538,7 @@ func TestInitiateNormalTransaction(t *testing.T) {
mockAccountService.On("TokenTransfer").Return(tt.TransferResponse, nil) mockAccountService.On("TokenTransfer").Return(tt.TransferResponse, nil)
// Call the method under test // Call the method under test
res, _ := h.InitiateNormalTransaction(ctx, "transaction_reset_amount", []byte("")) res, _ := h.InitiateTransaction(ctx, "transaction_reset_amount", []byte(""))
// Assert that no errors occurred // Assert that no errors occurred
assert.NoError(t, err) assert.NoError(t, err)

View File

@ -20,7 +20,7 @@ func (h *MenuHandlers) CheckTransactions(ctx context.Context, sym string, input
} }
flag_no_transfers, _ := h.flagManager.GetFlag("flag_no_transfers") flag_no_transfers, _ := h.flagManager.GetFlag("flag_no_transfers")
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error") flag_api_error, _ := h.flagManager.GetFlag("flag_api_error")
userStore := h.userdataStore userStore := h.userdataStore
logdb := h.logDb logdb := h.logDb
@ -33,11 +33,11 @@ func (h *MenuHandlers) CheckTransactions(ctx context.Context, sym string, input
// Fetch transactions from the API using the public key // Fetch transactions from the API using the public key
transactionsResp, err := h.accountService.FetchTransactions(ctx, string(publicKey)) transactionsResp, err := h.accountService.FetchTransactions(ctx, string(publicKey))
if err != nil { if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error) res.FlagSet = append(res.FlagSet, flag_api_error)
logg.ErrorCtxf(ctx, "failed on FetchTransactions", "error", err) logg.ErrorCtxf(ctx, "failed on FetchTransactions", "error", err)
return res, err return res, err
} }
res.FlagReset = append(res.FlagReset, flag_api_call_error) res.FlagReset = append(res.FlagReset, flag_api_error)
// Return if there are no transactions // Return if there are no transactions
if len(transactionsResp) == 0 { if len(transactionsResp) == 0 {

View File

@ -3,12 +3,10 @@ package application
import ( import (
"context" "context"
"fmt" "fmt"
"sort"
"strings" "strings"
"git.defalsify.org/vise.git/db" "git.defalsify.org/vise.git/db"
"git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/resource"
"git.grassecon.net/grassrootseconomics/sarafu-vise/config"
"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"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
@ -17,9 +15,8 @@ import (
// ManageVouchers retrieves the token holdings from the API using the "PublicKey" and // ManageVouchers retrieves the token holdings from the API using the "PublicKey" and
// 1. sets the first as the default voucher if no active voucher is set. // 1. sets the first as the default voucher if no active voucher is set.
// 2. Stores list of filtered ordered vouchers (exclude the active voucher) // 2. Stores list of vouchers
// 3. Stores list of ordered vouchers (all vouchers) // 3. updates the balance of the active voucher
// 4. updates the balance of the active voucher
func (h *MenuHandlers) ManageVouchers(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *MenuHandlers) ManageVouchers(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
userStore := h.userdataStore userStore := h.userdataStore
@ -32,7 +29,6 @@ func (h *MenuHandlers) ManageVouchers(ctx context.Context, sym string, input []b
flag_no_active_voucher, _ := h.flagManager.GetFlag("flag_no_active_voucher") flag_no_active_voucher, _ := h.flagManager.GetFlag("flag_no_active_voucher")
flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
flag_multiple_voucher, _ := h.flagManager.GetFlag("flag_multiple_voucher")
publicKey, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY) publicKey, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY)
if err != nil { if err != nil {
@ -49,37 +45,10 @@ func (h *MenuHandlers) ManageVouchers(ctx context.Context, sym string, input []b
res.FlagReset = append(res.FlagReset, flag_api_error) res.FlagReset = append(res.FlagReset, flag_api_error)
if len(vouchersResp) == 0 { if len(vouchersResp) == 0 {
// clear the current active voucher data if it exists
_, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM)
if err == nil {
firstVoucherMap := map[storedb.DataTyp]string{
storedb.DATA_ACTIVE_SYM: "",
storedb.DATA_ACTIVE_BAL: "",
storedb.DATA_ACTIVE_DECIMAL: "",
storedb.DATA_ACTIVE_ADDRESS: "",
}
for key, value := range firstVoucherMap {
if err := userStore.WriteEntry(ctx, sessionId, key, []byte(value)); err != nil {
logg.ErrorCtxf(ctx, "Failed to reset active voucher data", "key", key, "error", err)
return res, err
}
}
logg.InfoCtxf(ctx, "Default voucher reset")
}
res.FlagSet = append(res.FlagSet, flag_no_active_voucher) res.FlagSet = append(res.FlagSet, flag_no_active_voucher)
return res, nil return res, nil
} }
// only set the flag if the user has a single voucher
if len(vouchersResp) == 1 {
res.FlagReset = append(res.FlagReset, flag_multiple_voucher)
} else {
res.FlagSet = append(res.FlagSet, flag_multiple_voucher)
}
res.FlagReset = append(res.FlagReset, flag_no_active_voucher) res.FlagReset = append(res.FlagReset, flag_no_active_voucher)
// add a variable to filter out the active voucher // add a variable to filter out the active voucher
@ -155,36 +124,7 @@ func (h *MenuHandlers) ManageVouchers(ctx context.Context, sym string, input []b
} }
} }
// Build stable voucher priority (lower index = higher priority) // Filter out the active voucher from vouchersResp
stablePriority := make(map[string]int)
stableAddresses := config.StableVoucherAddresses()
for i, addr := range stableAddresses {
stablePriority[addr] = i
}
// Helper: order vouchers (stable first, priority-based)
orderVouchers := func(vouchers []dataserviceapi.TokenHoldings) []dataserviceapi.TokenHoldings {
stable := make([]dataserviceapi.TokenHoldings, 0)
nonStable := make([]dataserviceapi.TokenHoldings, 0)
for _, v := range vouchers {
if isStableVoucher(v.TokenAddress) {
stable = append(stable, v)
} else {
nonStable = append(nonStable, v)
}
}
sort.SliceStable(stable, func(i, j int) bool {
ai := stablePriority[stable[i].TokenAddress]
aj := stablePriority[stable[j].TokenAddress]
return ai < aj
})
return append(stable, nonStable...)
}
// Remove active voucher
filteredVouchers := make([]dataserviceapi.TokenHoldings, 0, len(vouchersResp)) filteredVouchers := make([]dataserviceapi.TokenHoldings, 0, len(vouchersResp))
for _, v := range vouchersResp { for _, v := range vouchersResp {
if v.TokenSymbol != activeSymStr { if v.TokenSymbol != activeSymStr {
@ -192,11 +132,8 @@ func (h *MenuHandlers) ManageVouchers(ctx context.Context, sym string, input []b
} }
} }
// Order remaining vouchers // Store all voucher data (excluding the current active voucher)
orderedFilteredVouchers := orderVouchers(filteredVouchers) data := store.ProcessVouchers(filteredVouchers)
// Process & store
data := store.ProcessVouchers(orderedFilteredVouchers)
dataMap := map[storedb.DataTyp]string{ dataMap := map[storedb.DataTyp]string{
storedb.DATA_VOUCHER_SYMBOLS: data.Symbols, storedb.DATA_VOUCHER_SYMBOLS: data.Symbols,
@ -207,26 +144,6 @@ func (h *MenuHandlers) ManageVouchers(ctx context.Context, sym string, input []b
// Write data entries // Write data entries
for key, value := range dataMap { 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)
}
}
// Order all vouchers
orderedVouchers := orderVouchers(vouchersResp)
// Process ALL vouchers (stable first)
orderedVoucherData := store.ProcessVouchers(orderedVouchers)
orderedVoucherDataMap := map[storedb.DataTyp]string{
storedb.DATA_ORDERED_VOUCHER_SYMBOLS: orderedVoucherData.Symbols,
storedb.DATA_ORDERED_VOUCHER_BALANCES: orderedVoucherData.Balances,
storedb.DATA_ORDERED_VOUCHER_DECIMALS: orderedVoucherData.Decimals,
storedb.DATA_ORDERED_VOUCHER_ADDRESSES: orderedVoucherData.Addresses,
}
// Write data entries
for key, value := range orderedVoucherDataMap {
if err := userStore.WriteEntry(ctx, sessionId, key, []byte(value)); err != nil { 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) logg.ErrorCtxf(ctx, "Failed to write data entry for sessionId: %s", sessionId, "key", key, "error", err)
continue continue
@ -237,7 +154,6 @@ func (h *MenuHandlers) ManageVouchers(ctx context.Context, sym string, input []b
} }
// GetVoucherList fetches the list of vouchers from the store and formats them. // GetVoucherList fetches the list of vouchers from the store and formats them.
// does not include the active voucher and is used in select_voucher and pay_debt
func (h *MenuHandlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *MenuHandlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
sessionId, ok := ctx.Value("SessionId").(string) sessionId, ok := ctx.Value("SessionId").(string)
@ -245,18 +161,8 @@ func (h *MenuHandlers) GetVoucherList(ctx context.Context, sym string, input []b
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
userStore := h.userdataStore userStore := h.userdataStore
// Fetch session data
_, _, activeSym, _, _, _, err := h.getSessionData(ctx, sessionId)
if err != nil {
return res, nil
}
// Read vouchers from the store // Read vouchers from the store
voucherData, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_VOUCHER_SYMBOLS) voucherData, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_VOUCHER_SYMBOLS)
logg.InfoCtxf(ctx, "reading voucherData in GetVoucherList", "sessionId", sessionId, "key", storedb.DATA_VOUCHER_SYMBOLS, "voucherData", voucherData) logg.InfoCtxf(ctx, "reading voucherData in GetVoucherList", "sessionId", sessionId, "key", storedb.DATA_VOUCHER_SYMBOLS, "voucherData", voucherData)
@ -265,16 +171,6 @@ func (h *MenuHandlers) GetVoucherList(ctx context.Context, sym string, input []b
return res, err return res, err
} }
if len(voucherData) == 0 {
if sym == "get_paydebt_voucher_list" {
res.Content = l.Get("You need another voucher to proceed. Only found %s.", string(activeSym))
} else {
res.Content = l.Get("Your active voucher %s is already set", string(activeSym))
}
return res, nil
}
voucherBalances, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_VOUCHER_BALANCES) voucherBalances, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_VOUCHER_BALANCES)
logg.InfoCtxf(ctx, "reading voucherBalances in GetVoucherList", "sessionId", sessionId, "key", storedb.DATA_VOUCHER_BALANCES, "voucherBalances", voucherBalances) logg.InfoCtxf(ctx, "reading voucherBalances in GetVoucherList", "sessionId", sessionId, "key", storedb.DATA_VOUCHER_BALANCES, "voucherBalances", voucherBalances)
if err != nil { if err != nil {
@ -287,7 +183,7 @@ func (h *MenuHandlers) GetVoucherList(ctx context.Context, sym string, input []b
logg.InfoCtxf(ctx, "final output for GetVoucherList", "sessionId", sessionId, "finalOutput", finalOutput) logg.InfoCtxf(ctx, "final output for GetVoucherList", "sessionId", sessionId, "finalOutput", finalOutput)
res.Content = l.Get("Select number or symbol from your vouchers:\n%s", finalOutput) res.Content = finalOutput
return res, nil return res, nil
} }
@ -323,8 +219,8 @@ func (h *MenuHandlers) ViewVoucher(ctx context.Context, sym string, input []byte
return res, nil return res, nil
} }
if err := store.StoreTransactionVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil { if err := store.StoreTemporaryVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil {
logg.ErrorCtxf(ctx, "failed on StoreTransactionVoucher", "error", err) logg.ErrorCtxf(ctx, "failed on StoreTemporaryVoucher", "error", err)
return res, err return res, err
} }
@ -352,9 +248,9 @@ func (h *MenuHandlers) SetVoucher(ctx context.Context, sym string, input []byte)
} }
// Get temporary data // Get temporary data
tempData, err := store.GetTransactionVoucherData(ctx, h.userdataStore, sessionId) tempData, err := store.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId)
if err != nil { if err != nil {
logg.ErrorCtxf(ctx, "failed on GetTransactionVoucherData", "error", err) logg.ErrorCtxf(ctx, "failed on GetTemporaryVoucherData", "error", err)
return res, err return res, err
} }
@ -402,50 +298,3 @@ func (h *MenuHandlers) GetVoucherDetails(ctx context.Context, sym string, input
return res, nil return res, nil
} }
// ValidateCreditVoucher sets the selected voucher as the active transaction voucher
func (h *MenuHandlers) ValidateCreditVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher")
res.FlagReset = append(res.FlagReset, flag_incorrect_voucher)
inputStr := string(input)
if inputStr == "0" || inputStr == "99" || inputStr == "88" || inputStr == "98" {
return res, nil
}
userStore := h.userdataStore
metadata, err := store.GetOrderedVoucherData(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 transaction voucher data
if err := store.StoreTransactionVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil {
logg.ErrorCtxf(ctx, "failed on StoreTransactionVoucher", "error", err)
return res, err
}
// Store the state of the custom transaction voucher
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_TRANSACTION_CUSTOM_VOUCHER_STATE, []byte("1"))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write custom transaction voucher", "key", storedb.DATA_TRANSACTION_CUSTOM_VOUCHER_STATE, "error", err)
return res, err
}
return res, nil
}

View File

@ -81,13 +81,11 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountService)
ls.DbRs.AddLocalFunc("check_account_status", appHandlers.CheckAccountStatus) ls.DbRs.AddLocalFunc("check_account_status", appHandlers.CheckAccountStatus)
ls.DbRs.AddLocalFunc("authorize_account", appHandlers.Authorize) ls.DbRs.AddLocalFunc("authorize_account", appHandlers.Authorize)
ls.DbRs.AddLocalFunc("quit", appHandlers.Quit) ls.DbRs.AddLocalFunc("quit", appHandlers.Quit)
ls.DbRs.AddLocalFunc("calc_credit_debt", appHandlers.CalculateCreditAndDebt)
ls.DbRs.AddLocalFunc("check_balance", appHandlers.CheckBalance) ls.DbRs.AddLocalFunc("check_balance", appHandlers.CheckBalance)
ls.DbRs.AddLocalFunc("validate_recipient", appHandlers.ValidateRecipient) ls.DbRs.AddLocalFunc("validate_recipient", appHandlers.ValidateRecipient)
ls.DbRs.AddLocalFunc("transaction_reset", appHandlers.TransactionReset) ls.DbRs.AddLocalFunc("transaction_reset", appHandlers.TransactionReset)
ls.DbRs.AddLocalFunc("invite_valid_recipient", appHandlers.InviteValidRecipient) ls.DbRs.AddLocalFunc("invite_valid_recipient", appHandlers.InviteValidRecipient)
ls.DbRs.AddLocalFunc("send_max_amount", appHandlers.MaxAmount) ls.DbRs.AddLocalFunc("max_amount", appHandlers.MaxAmount)
ls.DbRs.AddLocalFunc("credit_max_amount", appHandlers.MaxAmount)
ls.DbRs.AddLocalFunc("validate_amount", appHandlers.ValidateAmount) ls.DbRs.AddLocalFunc("validate_amount", appHandlers.ValidateAmount)
ls.DbRs.AddLocalFunc("reset_transaction_amount", appHandlers.ResetTransactionAmount) ls.DbRs.AddLocalFunc("reset_transaction_amount", appHandlers.ResetTransactionAmount)
ls.DbRs.AddLocalFunc("get_recipient", appHandlers.GetRecipient) ls.DbRs.AddLocalFunc("get_recipient", appHandlers.GetRecipient)
@ -105,14 +103,12 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountService)
ls.DbRs.AddLocalFunc("get_profile_info", appHandlers.GetProfileInfo) ls.DbRs.AddLocalFunc("get_profile_info", appHandlers.GetProfileInfo)
ls.DbRs.AddLocalFunc("verify_yob", appHandlers.VerifyYob) ls.DbRs.AddLocalFunc("verify_yob", appHandlers.VerifyYob)
ls.DbRs.AddLocalFunc("reset_incorrect_date_format", appHandlers.ResetIncorrectYob) ls.DbRs.AddLocalFunc("reset_incorrect_date_format", appHandlers.ResetIncorrectYob)
ls.DbRs.AddLocalFunc("normal_transaction_preview", appHandlers.NormalTransactionPreview) ls.DbRs.AddLocalFunc("initiate_transaction", appHandlers.InitiateTransaction)
ls.DbRs.AddLocalFunc("initiate_normal_transaction", appHandlers.InitiateNormalTransaction)
ls.DbRs.AddLocalFunc("confirm_pin_change", appHandlers.ConfirmPinChange) ls.DbRs.AddLocalFunc("confirm_pin_change", appHandlers.ConfirmPinChange)
ls.DbRs.AddLocalFunc("quit_with_help", appHandlers.QuitWithHelp) ls.DbRs.AddLocalFunc("quit_with_help", appHandlers.QuitWithHelp)
ls.DbRs.AddLocalFunc("fetch_community_balance", appHandlers.FetchCommunityBalance) ls.DbRs.AddLocalFunc("fetch_community_balance", appHandlers.FetchCommunityBalance)
ls.DbRs.AddLocalFunc("manage_vouchers", appHandlers.ManageVouchers) ls.DbRs.AddLocalFunc("manage_vouchers", appHandlers.ManageVouchers)
ls.DbRs.AddLocalFunc("get_voucher_list", appHandlers.GetVoucherList) ls.DbRs.AddLocalFunc("get_vouchers", appHandlers.GetVoucherList)
ls.DbRs.AddLocalFunc("get_paydebt_voucher_list", appHandlers.GetVoucherList)
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)
@ -140,24 +136,6 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountService)
ls.DbRs.AddLocalFunc("swap_max_limit", appHandlers.SwapMaxLimit) ls.DbRs.AddLocalFunc("swap_max_limit", appHandlers.SwapMaxLimit)
ls.DbRs.AddLocalFunc("swap_preview", appHandlers.SwapPreview) ls.DbRs.AddLocalFunc("swap_preview", appHandlers.SwapPreview)
ls.DbRs.AddLocalFunc("initiate_swap", appHandlers.InitiateSwap) ls.DbRs.AddLocalFunc("initiate_swap", appHandlers.InitiateSwap)
ls.DbRs.AddLocalFunc("transaction_swap_preview", appHandlers.TransactionSwapPreview)
ls.DbRs.AddLocalFunc("transaction_initiate_swap", appHandlers.TransactionInitiateSwap)
ls.DbRs.AddLocalFunc("clear_trans_type_flag", appHandlers.ClearTransactionTypeFlag)
ls.DbRs.AddLocalFunc("get_mpesa_max_limit", appHandlers.GetMpesaMaxLimit)
ls.DbRs.AddLocalFunc("get_mpesa_preview", appHandlers.GetMpesaPreview)
ls.DbRs.AddLocalFunc("initiate_get_mpesa", appHandlers.InitiateGetMpesa)
ls.DbRs.AddLocalFunc("send_mpesa_min_limit", appHandlers.SendMpesaMinLimit)
ls.DbRs.AddLocalFunc("send_mpesa_preview", appHandlers.SendMpesaPreview)
ls.DbRs.AddLocalFunc("initiate_send_mpesa", appHandlers.InitiateSendMpesa)
ls.DbRs.AddLocalFunc("calculate_max_pay_debt", appHandlers.CalculateMaxPayDebt)
ls.DbRs.AddLocalFunc("confirm_debt_removal", appHandlers.ConfirmDebtRemoval)
ls.DbRs.AddLocalFunc("initiate_pay_debt", appHandlers.InitiatePayDebt)
ls.DbRs.AddLocalFunc("get_ordered_vouchers", appHandlers.GetOrderedVouchers)
ls.DbRs.AddLocalFunc("pool_deposit_max_amount", appHandlers.PoolDepositMaxAmount)
ls.DbRs.AddLocalFunc("confirm_pool_deposit", appHandlers.ConfirmPoolDeposit)
ls.DbRs.AddLocalFunc("initiate_pool_deposit", appHandlers.InitiatePoolDeposit)
ls.DbRs.AddLocalFunc("validate_credit_voucher", appHandlers.ValidateCreditVoucher)
ls.first = appHandlers.Init ls.first = appHandlers.Init
return appHandlers, nil return appHandlers, nil

View File

@ -5,19 +5,19 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "3", "input": "2",
"expectedContent": "My vouchers\n1:Select voucher\n2:Voucher details\n0:Back" "expectedContent": "My vouchers\n1:Select voucher\n2:Voucher details\n0:Back"
}, },
{ {
"input": "1", "input": "1",
"expectedContent": "Select number or symbol from your vouchers:\n1:SRF\n0:Back\n99:Quit" "expectedContent": "Select number or symbol from your vouchers:\n1SRF\n0:Back\n99:Quit"
}, },
{ {
"input": "", "input": "",
"expectedContent": "Select number or symbol from your vouchers:\n1:SRF\n0:Back\n99:Quit" "expectedContent": "Select number or symbol from your vouchers:\n1SRF\n0:Back\n99:Quit"
}, },
{ {
"input": "1", "input": "1",
@ -29,7 +29,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -38,15 +38,15 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "3", "input": "2",
"expectedContent": "My vouchers\n1:Select voucher\n2:Voucher details\n0:Back" "expectedContent": "My vouchers\n1:Select voucher\n2:Voucher details\n0:Back"
}, },
{ {
"input": "1", "input": "1",
"expectedContent": "Select number or symbol from your vouchers:\n1:SRF\n0:Back\n99:Quit" "expectedContent": "Select number or symbol from your vouchers:\n1SRF\n0:Back\n99:Quit"
}, },
{ {
"input": "SRF", "input": "SRF",
@ -58,7 +58,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -67,10 +67,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -95,7 +95,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -104,10 +104,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -136,7 +136,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -145,10 +145,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -177,7 +177,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -186,10 +186,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -210,7 +210,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -219,10 +219,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -235,7 +235,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -244,10 +244,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -280,7 +280,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -289,10 +289,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -325,7 +325,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -334,10 +334,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -382,7 +382,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -391,10 +391,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -419,7 +419,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -428,10 +428,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -460,7 +460,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -469,10 +469,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -497,7 +497,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -506,10 +506,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -534,7 +534,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -543,10 +543,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -571,7 +571,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -580,10 +580,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -604,7 +604,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
@ -613,10 +613,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {

View File

@ -5,10 +5,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -49,7 +49,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
} }

View File

@ -5,10 +5,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -53,7 +53,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
} }

View File

@ -5,10 +5,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -45,7 +45,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
} }

View File

@ -5,10 +5,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -37,7 +37,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
} }

View File

@ -5,10 +5,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -33,7 +33,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
} }

View File

@ -5,10 +5,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -41,7 +41,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
} }

View File

@ -5,10 +5,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {
@ -61,7 +61,7 @@
}, },
{ {
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
} }

View File

@ -57,7 +57,7 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "1", "input": "1",
@ -86,10 +86,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "6", "input": "4",
"expectedContent": "For more help,please call: 0757628885" "expectedContent": "For more help,please call: 0757628885"
} }
] ]
@ -99,7 +99,7 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "9", "input": "9",
@ -112,10 +112,10 @@
"steps": [ "steps": [
{ {
"input": "", "input": "",
"expectedContent": "{balance}\n\n1:Send\n2:Swap\n3:My Vouchers\n4:Select pool\n5:My Account\n6:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
}, },
{ {
"input": "5", "input": "3",
"expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back"
}, },
{ {

View File

@ -1 +1,2 @@
{{.send_max_amount}} Maximum amount: {{.max_amount}}
Enter amount:

View File

@ -1,8 +1,7 @@
LOAD reset_transaction_amount 10 LOAD reset_transaction_amount 0
RELOAD reset_transaction_amount LOAD max_amount 40
LOAD send_max_amount 0 RELOAD max_amount
RELOAD send_max_amount MAP max_amount
MAP send_max_amount
MOUT back 0 MOUT back 0
HALT HALT
LOAD validate_amount 64 LOAD validate_amount 64
@ -10,7 +9,7 @@ RELOAD validate_amount
CATCH api_failure flag_api_call_error 1 CATCH api_failure flag_api_call_error 1
CATCH invalid_amount flag_invalid_amount 1 CATCH invalid_amount flag_invalid_amount 1
INCMP _ 0 INCMP _ 0
LOAD get_recipient 100 LOAD get_recipient 0
LOAD get_sender 64 LOAD get_sender 64
LOAD get_amount 32 LOAD get_amount 32
INCMP transaction_pin * INCMP transaction_pin *

View File

@ -1 +1,2 @@
{{.send_max_amount}} Kiwango cha juu: {{.max_amount}}
Weka kiwango:

View File

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

View File

@ -1,10 +0,0 @@
LOAD reset_transaction_amount 10
RELOAD reset_transaction_amount
MAP calculate_max_pay_debt
MOUT back 0
HALT
LOAD confirm_debt_removal 140
RELOAD confirm_debt_removal
CATCH invalid_pay_debt_amount flag_invalid_amount 1
INCMP _ 0
INCMP confirm_debt_removal *

View File

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

View File

@ -1,10 +0,0 @@
MAP confirm_debt_removal
MOUT back 0
MOUT quit 9
HALT
LOAD authorize_account 6
RELOAD authorize_account
CATCH incorrect_pin flag_incorrect_pin 1
INCMP _ 0
INCMP quit 9
INCMP initiate_pay_debt *

View File

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

View File

@ -1,16 +0,0 @@
LOAD reset_transaction_amount 10
RELOAD reset_transaction_amount
LOAD credit_max_amount 160
RELOAD credit_max_amount
CATCH api_failure flag_api_call_error 1
MAP credit_max_amount
MOUT back 0
HALT
LOAD clear_trans_type_flag 6
RELOAD clear_trans_type_flag
CATCH transaction_swap flag_swap_transaction 1
LOAD validate_amount 64
RELOAD validate_amount
CATCH invalid_amount flag_invalid_amount 1
INCMP _ 0
INCMP transaction_pin *

View File

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

View File

@ -1 +0,0 @@
Weka nambari ya simu/anwani/lakabu:

View File

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

View File

@ -1,15 +0,0 @@
LOAD get_ordered_vouchers 0
MAP get_ordered_vouchers
MOUT back 0
MOUT quit 99
MNEXT next 88
MPREV prev 98
HALT
INCMP > 88
INCMP < 98
INCMP _ 0
INCMP quit 99
LOAD validate_credit_voucher 67
RELOAD validate_credit_voucher
CATCH . flag_incorrect_voucher 1
INCMP credit_amount *

View File

@ -1 +0,0 @@
Pool deposit

View File

@ -1 +0,0 @@
Weka kwa bwawa

View File

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

View File

@ -1,19 +0,0 @@
CATCH no_voucher flag_no_active_voucher 1
LOAD get_ordered_vouchers 0
MAP get_ordered_vouchers
MOUT back 0
MOUT quit 99
MNEXT next 88
MPREV prev 98
HALT
INCMP > 88
INCMP < 98
INCMP _ 0
INCMP quit 99
LOAD get_mpesa_max_limit 89
RELOAD get_mpesa_max_limit
CATCH . flag_incorrect_voucher 1
CATCH low_withdraw_mpesa_amount flag_incorrect_pool 1
CATCH low_withdraw_mpesa_amount flag_low_swap_amount 1
CATCH low_withdraw_mpesa_amount flag_api_call_error 1
INCMP mpesa_max_limit *

View File

@ -1,3 +0,0 @@
{{.get_mpesa_preview}}
Please enter your PIN to confirm. You will get an SMS shortly:

View File

@ -1,10 +0,0 @@
MAP get_mpesa_preview
MOUT back 0
MOUT quit 9
HALT
LOAD authorize_account 6
RELOAD authorize_account
CATCH incorrect_pin flag_incorrect_pin 1
INCMP _ 0
INCMP quit 9
INCMP initiate_get_mpesa *

View File

@ -1,3 +0,0 @@
{{.get_mpesa_preview}}
Tafadhali weka PIN yako kudhibitisha. Utapokea ujumbe wa SMS:

View File

@ -1 +0,0 @@
Withdraw

View File

@ -1 +0,0 @@
Pokea M-Pesa

View File

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

View File

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

View File

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

View File

@ -1 +0,0 @@
Amount {{.transaction_swap_preview}} is invalid, please try again:

View File

@ -1,7 +0,0 @@
MAP transaction_swap_preview
RELOAD reset_transaction_amount
MOUT retry 1
MOUT quit 9
HALT
INCMP ^ 1
INCMP quit 9

View File

@ -1 +0,0 @@
Kiwango {{.transaction_swap_preview}} sio sahihi, tafadhali weka tena:

View File

@ -1 +0,0 @@
Amount {{.get_mpesa_preview}} is invalid, please try again:

View File

@ -1,7 +0,0 @@
MAP get_mpesa_preview
RELOAD reset_transaction_amount
MOUT retry 1
MOUT quit 9
HALT
INCMP _ 1
INCMP quit 9

View File

@ -1 +0,0 @@
Kiwango {{.get_mpesa_preview}} sio sahihi, tafadhali weka tena:

View File

@ -1 +0,0 @@
Amount {{.confirm_debt_removal}} is invalid, please try again:

View File

@ -1,7 +0,0 @@
MAP confirm_debt_removal
RELOAD reset_transaction_amount
MOUT retry 1
MOUT quit 9
HALT
INCMP _ 1
INCMP quit 9

View File

@ -1 +0,0 @@
Kiwango {{.confirm_debt_removal}} sio sahihi, tafadhali weka tena:

View File

@ -1 +0,0 @@
Amount {{.confirm_pool_deposit}} is invalid, please try again:

View File

@ -1,7 +0,0 @@
MAP confirm_pool_deposit
RELOAD reset_transaction_amount
MOUT retry 1
MOUT quit 9
HALT
INCMP _ 1
INCMP quit 9

View File

@ -1 +0,0 @@
Kiwango {{.confirm_pool_deposit}} sio sahihi, tafadhali weka tena:

View File

@ -1 +0,0 @@
Amount {{.send_mpesa_preview}} is invalid, please try again:

View File

@ -1,6 +0,0 @@
MAP send_mpesa_preview
MOUT retry 1
MOUT quit 9
HALT
INCMP ^ 1
INCMP quit 9

View File

@ -1 +0,0 @@
Kiwango {{.send_mpesa_preview}} sio sahihi, tafadhali weka tena:

View File

@ -32,7 +32,7 @@ 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." 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 ujumbe wakati %s %s yako itakapobadilishwa kuwa %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"
@ -45,69 +45,3 @@ msgstr "Jina: %s\nSarafu: %s"
msgid "Only USD vouchers are allowed to mpesa.sarafu.eth." msgid "Only USD vouchers are allowed to mpesa.sarafu.eth."
msgstr "Ni sarafu za USD pekee zinazoruhusiwa kwa mpesa.sarafu.eth." msgstr "Ni sarafu za USD pekee zinazoruhusiwa kwa mpesa.sarafu.eth."
msgid "Maximum amount: %s %s\nEnter amount:"
msgstr "Kiwango cha juu: %s %s\nWeka kiwango:"
msgid "Credit Available: %s %s\n(You can swap up to %s %s -> %s %s).\nEnter %s amount:"
msgstr "Kiwango kinachopatikana: %s %s\n(Unaweza kubadilisha hadi %s %s -> %s %s)\nWeka kiwango cha %s:"
msgid "%s will receive %s %s"
msgstr "%s atapokea %s %s"
msgid "Enter the amount of M-Pesa to get: (Max %s Ksh)\n"
msgstr "Weka kiasi cha M-Pesa cha kupata: (Kikomo %s Ksh)\n"
msgid "You are sending %s %s in order to receive ~ %s ksh"
msgstr "Unatuma ~ %s %s ili upokee %s ksh"
msgid "Your request has been sent. Please await confirmation"
msgstr "Ombi lako limetumwa. Tafadhali subiri"
msgid "Enter the amount of M-Pesa to send: (Minimum %s Ksh)\n"
msgstr "Weka kiasi cha M-Pesa cha kutuma: (Kima cha chini %s Ksh)\n"
msgid "You will get a prompt for your Mpesa PIN shortly to send %s ksh and receive ~ %s %s"
msgstr "Utapokea kidokezo cha PIN yako ya Mpesa hivi karibuni kutuma %s ksh na kupokea ~ %s %s"
msgid "Your request has been sent. Thank you for using Sarafu"
msgstr "Ombi lako limetumwa. Asante kwa kutumia huduma ya Sarafu"
msgid "You can remove a max of %s %s from '%s' pool\nEnter amount of %s:(Max: %s)"
msgstr "Unaweza kuondoa kiwango cha juu cha %s %s kutoka kwenye '%s'\n\nWeka kiwango cha %s:(Kikomo: %s)"
msgid "Please confirm that you will use %s %s to remove your debt of %s %s\nEnter your PIN:"
msgstr "Tafadhali thibitisha kwamba utatumia %s %s kulipa deni lako la %s %s.\nWeka PIN yako:"
msgid "Your active voucher %s is already set"
msgstr "Sarafu yako %s ishachaguliwa"
msgid "Select number or symbol from your vouchers:\n%s"
msgstr "Chagua nambari au ishara kutoka kwa sarafu zako:\n%s"
msgid "You will deposit %s %s into %s\n"
msgstr "Utaweka %s %s kwenye %s\n"
msgid "Your request has been sent. You will receive an SMS when %s %s has been deposited into %s."
msgstr "Ombi lako limetumwa. Utapokea ujumbe wakati %s %s itawekwa kwenye %s."
msgid "%s will receive %s %s from %s"
msgstr %s atapokea %s %s kutoka kwa %s"
msgid "You need another voucher to proceed. Only found %s."
msgstr "Unahitaji kua na sarafu nyingine. Tumepata tu %s."
msgid "Maximum: %s %s\n\nEnter amount of %s to swap for %s:"
msgstr "Kikimo: %s %s\n\nWeka kiasi cha %s kitakacho badilishwa kua %s:"
msgid "You will swap %s %s for %s %s:"
msgstr "Utabadilisha %s %s kua %s %s:"
msgid "Your request has been sent. You will receive an SMS when your debt of %s %s has been removed from %s."
msgstr "Ombi lako limetumwa. Utapokea ujumbe wakati deni lako la %s %s litatolewa kwa %s."
msgid "Enter the amount of Mpesa to withdraw: (Min: Ksh %s, Max %s Ksh)\n"
msgstr "Weka kiasi cha Mpesa utakacho toa: (Min: Ksh %s, Max %s Ksh)\n"
msgid "Enter the amount of credit to deposit: (Minimum %s Ksh)\n"
msgstr "Weka kiasi utakacho weka (Kima cha chini: %s Ksh)\n"

View File

@ -1 +0,0 @@
Available amount {{.calculate_max_pay_debt}} is too low, please choose a different voucher:

View File

@ -1,6 +0,0 @@
MAP calculate_max_pay_debt
MOUT back 0
MOUT quit 9
HALT
INCMP _ 0
INCMP quit 9

View File

@ -1 +0,0 @@
Kiasi kinachopatikana {{.calculate_max_pay_debt}} ni cha chini sana, tafadhali chagua sarafu tofauti:

View File

@ -1 +1 @@
Available amount {{.swap_max_limit}} is too low, please choose a different voucher: Available amount {{.swap_max_limit}} is too low, please try again:

View File

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

View File

@ -1 +0,0 @@
Available amount {{.get_mpesa_max_limit}} is too low, please choose a different voucher:

View File

@ -1,6 +0,0 @@
MAP get_mpesa_max_limit
MOUT back 0
MOUT quit 9
HALT
INCMP _ 0
INCMP quit 9

View File

@ -1 +0,0 @@
Kiasi kinachopatikana {{.get_mpesa_max_limit}} ni cha chini sana, tafadhali chagua sarafu tofauti:

View File

@ -3,24 +3,22 @@ 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 148 LOAD check_balance 128
RELOAD check_balance RELOAD check_balance
MAP check_balance MAP check_balance
MOUT send 1 MOUT send 1
MOUT swap 2 MOUT swap 2
MOUT vouchers 3 MOUT vouchers 3
MOUT select_pool 4 MOUT select_pool 4
MOUT mpesa 5 MOUT account 5
MOUT account 6 MOUT help 6
MOUT help 7
MOUT quit 9 MOUT quit 9
HALT HALT
INCMP credit_send 1 INCMP send 1
INCMP swap_to_list 2 INCMP swap_to_list 2
INCMP my_vouchers 3 INCMP my_vouchers 3
INCMP select_pool 4 INCMP select_pool 4
INCMP mpesa 5 INCMP my_account 5
INCMP my_account 6 INCMP help 6
INCMP help 7
INCMP quit 9 INCMP quit 9
INCMP . * INCMP . *

View File

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

View File

@ -1,18 +0,0 @@
LOAD calc_credit_debt 150
RELOAD calc_credit_debt
CATCH api_failure flag_api_call_error 1
MAP calc_credit_debt
MOUT pay_debt 1
MOUT deposit 2
MOUT get_mpesa 3
MOUT send_mpesa 4
MOUT back 0
MOUT quit 9
HALT
INCMP ^ 0
INCMP pay_debt 1
INCMP pool_deposit 2
INCMP get_mpesa 3
INCMP send_mpesa 4
INCMP quit 9
INCMP . *

View File

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

View File

@ -1,13 +0,0 @@
LOAD reset_transaction_amount 10
RELOAD reset_transaction_amount
MAP get_mpesa_max_limit
MOUT back 0
MOUT quit 9
HALT
INCMP _ 0
INCMP quit 9
LOAD get_mpesa_preview 90
RELOAD get_mpesa_preview
CATCH api_failure flag_api_call_error 1
CATCH invalid_get_mpesa_amount flag_invalid_amount 1
INCMP get_mpesa_confirmation *

View File

@ -1 +0,0 @@
M-Pesa

View File

@ -1 +0,0 @@
No stable voucher found

View File

@ -1,5 +0,0 @@
MOUT back 0
MOUT quit 9
HALT
INCMP ^ 0
INCMP quit 9

View File

@ -1 +0,0 @@
Hakuna sarafu thabiti iliyopatikana

View File

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

View File

@ -1,18 +0,0 @@
CATCH no_voucher flag_no_active_voucher 1
LOAD get_paydebt_voucher_list 0
MAP get_paydebt_voucher_list
MOUT back 0
MOUT quit 99
MNEXT next 88
MPREV prev 98
HALT
INCMP > 88
INCMP < 98
INCMP _ 0
INCMP quit 99
LOAD calculate_max_pay_debt 0
RELOAD calculate_max_pay_debt
CATCH . flag_incorrect_voucher 1
CATCH low_pay_debt_amount flag_low_swap_amount 1
CATCH low_pay_debt_amount flag_api_call_error 1
INCMP calculate_max_pay_debt *

View File

@ -1 +0,0 @@
Pay debt

View File

@ -1 +0,0 @@
Lipa deni

View File

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

View File

@ -1,16 +0,0 @@
CATCH no_voucher flag_no_active_voucher 1
LOAD get_ordered_vouchers 0
MAP get_ordered_vouchers
MOUT back 0
MOUT quit 99
MNEXT next 88
MPREV prev 98
HALT
INCMP > 88
INCMP < 98
INCMP _ 0
INCMP quit 99
LOAD pool_deposit_max_amount 120
RELOAD pool_deposit_max_amount
CATCH . flag_incorrect_voucher 1
INCMP pool_deposit_amount *

View File

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

View File

@ -1,10 +0,0 @@
LOAD reset_transaction_amount 10
RELOAD reset_transaction_amount
MAP pool_deposit_max_amount
MOUT back 0
HALT
LOAD confirm_pool_deposit 140
RELOAD confirm_pool_deposit
CATCH invalid_pool_deposit_amount flag_invalid_amount 1
INCMP _ 0
INCMP pool_deposit_confirmation *

View File

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

View File

@ -1,10 +0,0 @@
MAP confirm_pool_deposit
MOUT back 0
MOUT quit 9
HALT
LOAD authorize_account 6
RELOAD authorize_account
CATCH incorrect_pin flag_incorrect_pin 1
INCMP _ 0
INCMP quit 9
INCMP pool_deposit_initiated *

View File

@ -1,2 +0,0 @@
{{.confirm_pool_deposit}}
Tafadhali weka PIN yako kudhibitisha:

View File

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

Some files were not shown because too many files have changed in this diff Show More