Compare commits
4 Commits
5a09d33be0
...
c90d3cd731
| Author | SHA1 | Date | |
|---|---|---|---|
| c90d3cd731 | |||
| cda2d49f3e | |||
| 4492f8087a | |||
| 0f8c2f9270 |
@ -14,6 +14,7 @@ import (
|
|||||||
"git.grassecon.net/grassrootseconomics/sarafu-vise/store"
|
"git.grassecon.net/grassrootseconomics/sarafu-vise/store"
|
||||||
storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db"
|
storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db"
|
||||||
"github.com/grassrootseconomics/ethutils"
|
"github.com/grassrootseconomics/ethutils"
|
||||||
|
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
|
||||||
"gopkg.in/leonelquinteros/gotext.v1"
|
"gopkg.in/leonelquinteros/gotext.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -89,20 +90,20 @@ func (h *MenuHandlers) handlePhoneNumber(ctx context.Context, sessionId, recipie
|
|||||||
return *res, err
|
return *res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
senderSym, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM)
|
senderActiveAddress, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_ADDRESS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logg.ErrorCtxf(ctx, "Failed to read sender activeSym", "error", err)
|
logg.ErrorCtxf(ctx, "Failed to read sender senderActiveAddress", "error", err)
|
||||||
return *res, err
|
return *res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
recipientActiveToken, _ := store.ReadEntry(ctx, formattedNumber, storedb.DATA_ACTIVE_SYM)
|
recipientActiveAddress, _ := store.ReadEntry(ctx, formattedNumber, storedb.DATA_ACTIVE_ADDRESS)
|
||||||
|
|
||||||
txType := "swap"
|
txType := "swap"
|
||||||
|
|
||||||
// recipient has no active token → normal transaction
|
// recipient has no active token → normal transaction
|
||||||
if recipientActiveToken == nil {
|
if recipientActiveAddress == nil {
|
||||||
txType = "normal"
|
txType = "normal"
|
||||||
} else if senderSym != nil && string(senderSym) == string(recipientActiveToken) {
|
} else if senderActiveAddress != nil && string(senderActiveAddress) == string(recipientActiveAddress) {
|
||||||
// recipient has active token same as sender → normal transaction
|
// recipient has active token same as sender → normal transaction
|
||||||
txType = "normal"
|
txType = "normal"
|
||||||
}
|
}
|
||||||
@ -113,13 +114,11 @@ func (h *MenuHandlers) handlePhoneNumber(ctx context.Context, sessionId, recipie
|
|||||||
return *res, err
|
return *res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// only save recipient’s active token if it exists
|
// save the recipient's phone number
|
||||||
if recipientActiveToken != nil {
|
if err := store.WriteEntry(ctx, sessionId, storedb.DATA_RECIPIENT_PHONE_NUMBER, []byte(formattedNumber)); err != nil {
|
||||||
if err := store.WriteEntry(ctx, sessionId, storedb.DATA_RECIPIENT_ACTIVE_TOKEN, recipientActiveToken); err != nil {
|
logg.ErrorCtxf(ctx, "Failed to write recipient's phone number", "type", txType, "error", err)
|
||||||
logg.ErrorCtxf(ctx, "Failed to write recipient active token", "error", err)
|
|
||||||
return *res, err
|
return *res, err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return *res, nil
|
return *res, nil
|
||||||
}
|
}
|
||||||
@ -219,7 +218,7 @@ func (h *MenuHandlers) TransactionReset(ctx context.Context, sym string, input [
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = store.WriteEntry(ctx, sessionId, storedb.DATA_RECIPIENT_ACTIVE_TOKEN, []byte(""))
|
err = store.WriteEntry(ctx, sessionId, storedb.DATA_RECIPIENT_PHONE_NUMBER, []byte(""))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
@ -264,6 +263,7 @@ func (h *MenuHandlers) MaxAmount(ctx context.Context, sym string, input []byte)
|
|||||||
}
|
}
|
||||||
|
|
||||||
flag_api_error, _ := h.flagManager.GetFlag("flag_api_error")
|
flag_api_error, _ := h.flagManager.GetFlag("flag_api_error")
|
||||||
|
flag_swap_transaction, _ := h.flagManager.GetFlag("flag_swap_transaction")
|
||||||
userStore := h.userdataStore
|
userStore := h.userdataStore
|
||||||
|
|
||||||
// Fetch session data
|
// Fetch session data
|
||||||
@ -277,16 +277,22 @@ func (h *MenuHandlers) MaxAmount(ctx context.Context, sym string, input []byte)
|
|||||||
|
|
||||||
// If normal transaction, return balance
|
// If normal transaction, return balance
|
||||||
if string(transactionType) == "normal" {
|
if string(transactionType) == "normal" {
|
||||||
|
res.FlagReset = append(res.FlagReset, flag_swap_transaction)
|
||||||
res.Content = fmt.Sprintf("%s %s", formattedBalance, string(activeSym))
|
res.Content = fmt.Sprintf("%s %s", formattedBalance, string(activeSym))
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get recipient token address
|
res.FlagSet = append(res.FlagSet, flag_swap_transaction)
|
||||||
recipientTokenAddress, err := h.getRecipientTokenAddress(ctx, sessionId)
|
|
||||||
|
// Get the recipient's phone number to read other data items
|
||||||
|
recipientPhoneNumber, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_RECIPIENT_PHONE_NUMBER)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// fallback to normal
|
// invalid state
|
||||||
res.Content = fmt.Sprintf("%s %s", formattedBalance, string(activeSym))
|
return res, err
|
||||||
return res, nil
|
}
|
||||||
|
recipientActiveSym, recipientActiveAddress, recipientActiveDecimal, err := h.getRecipientData(ctx, string(recipientPhoneNumber))
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve active pool address
|
// Resolve active pool address
|
||||||
@ -307,7 +313,7 @@ func (h *MenuHandlers) MaxAmount(ctx context.Context, sym string, input []byte)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calculate max swappable amount
|
// Calculate max swappable amount
|
||||||
maxStr, err := h.calculateSwapMaxAmount(ctx, activePoolAddress, activeAddress, recipientTokenAddress, publicKey, activeDecimal)
|
maxStr, err := h.calculateSwapMaxAmount(ctx, activePoolAddress, activeAddress, recipientActiveAddress, publicKey, activeDecimal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.FlagSet = append(res.FlagSet, flag_api_error)
|
res.FlagSet = append(res.FlagSet, flag_api_error)
|
||||||
return res, err
|
return res, err
|
||||||
@ -327,6 +333,20 @@ func (h *MenuHandlers) MaxAmount(ctx context.Context, sym string, input []byte)
|
|||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// save swap related data for the swap preview
|
||||||
|
metadata := &dataserviceapi.TokenHoldings{
|
||||||
|
TokenSymbol: string(recipientActiveSym),
|
||||||
|
Balance: formattedBalance, //not used
|
||||||
|
TokenDecimals: string(recipientActiveDecimal),
|
||||||
|
TokenAddress: string(recipientActiveAddress),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the active swap_to data
|
||||||
|
if err := store.UpdateSwapToVoucherData(ctx, userStore, sessionId, metadata); err != nil {
|
||||||
|
logg.ErrorCtxf(ctx, "failed on UpdateSwapToVoucherData", "error", err)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
res.Content = fmt.Sprintf("%s %s", maxStr, string(activeSym))
|
res.Content = fmt.Sprintf("%s %s", maxStr, string(activeSym))
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
@ -355,16 +375,28 @@ func (h *MenuHandlers) getSessionData(ctx context.Context, sessionId string) (tr
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
activeDecimal, err = store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_DECIMAL)
|
activeDecimal, err = store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_DECIMAL)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *MenuHandlers) getRecipientTokenAddress(ctx context.Context, sessionId string) ([]byte, error) {
|
func (h *MenuHandlers) getRecipientData(ctx context.Context, sessionId string) (recipientActiveSym, recipientActiveAddress, recipientActiveDecimal []byte, err error) {
|
||||||
store := h.userdataStore
|
store := h.userdataStore
|
||||||
recipientPhone, err := store.ReadEntry(ctx, sessionId, storedb.DATA_RECIPIENT_PHONE_NUMBER)
|
|
||||||
|
recipientActiveSym, err = store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return
|
||||||
}
|
}
|
||||||
return store.ReadEntry(ctx, string(recipientPhone), storedb.DATA_ACTIVE_ADDRESS)
|
recipientActiveAddress, err = store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_ADDRESS)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
recipientActiveDecimal, err = store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_DECIMAL)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *MenuHandlers) resolveActivePoolAddress(ctx context.Context, sessionId string) ([]byte, error) {
|
func (h *MenuHandlers) resolveActivePoolAddress(ctx context.Context, sessionId string) ([]byte, error) {
|
||||||
@ -570,3 +602,185 @@ func (h *MenuHandlers) InitiateTransaction(ctx context.Context, sym string, inpu
|
|||||||
res.FlagReset = append(res.FlagReset, flag_account_authorized)
|
res.FlagReset = append(res.FlagReset, flag_account_authorized)
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransactionSwapPreview displays the send swap preview and estimates
|
||||||
|
func (h *MenuHandlers) TransactionSwapPreview(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
|
||||||
|
|
||||||
|
recipientPhoneNumber, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_RECIPIENT_PHONE_NUMBER)
|
||||||
|
if err != nil {
|
||||||
|
// invalid state
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
swapData, err := store.ReadSwapPreviewData(ctx, userStore, sessionId)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
maxValue, err := strconv.ParseFloat(swapData.ActiveSwapMaxAmount, 64)
|
||||||
|
if err != nil {
|
||||||
|
logg.ErrorCtxf(ctx, "Failed to convert the swapMaxAmount to a float", "error", err)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
inputAmount, err := strconv.ParseFloat(inputStr, 64)
|
||||||
|
if err != nil || inputAmount > maxValue {
|
||||||
|
res.FlagSet = append(res.FlagSet, flag_invalid_amount)
|
||||||
|
res.Content = inputStr
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format the amount to 2 decimal places
|
||||||
|
formattedAmount, err := store.TruncateDecimalString(inputStr, 2)
|
||||||
|
if err != nil {
|
||||||
|
res.FlagSet = append(res.FlagSet, flag_invalid_amount)
|
||||||
|
res.Content = inputStr
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
finalAmountStr, err := store.ParseAndScaleAmount(formattedAmount, swapData.ActiveSwapFromDecimal)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SWAP_AMOUNT, []byte(finalAmountStr))
|
||||||
|
if err != nil {
|
||||||
|
logg.ErrorCtxf(ctx, "failed to write swap amount entry with", "key", storedb.DATA_ACTIVE_SWAP_AMOUNT, "value", finalAmountStr, "error", err)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
// store the user's input amount in the temporary value
|
||||||
|
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(inputStr))
|
||||||
|
if err != nil {
|
||||||
|
logg.ErrorCtxf(ctx, "failed to write swap amount entry with", "key", storedb.DATA_ACTIVE_SWAP_AMOUNT, "value", finalAmountStr, "error", err)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// call the API to get the quote
|
||||||
|
r, err := h.accountService.GetPoolSwapQuote(ctx, finalAmountStr, swapData.PublicKey, swapData.ActiveSwapFromAddress, swapData.ActivePoolAddress, swapData.ActiveSwapToAddress)
|
||||||
|
if err != nil {
|
||||||
|
flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
|
||||||
|
res.FlagSet = append(res.FlagSet, flag_api_error)
|
||||||
|
res.Content = l.Get("Your request failed. Please try again later.")
|
||||||
|
logg.ErrorCtxf(ctx, "failed on poolSwap", "error", err)
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale down the quoted amount
|
||||||
|
quoteAmountStr := store.ScaleDownBalance(r.OutValue, swapData.ActiveSwapToDecimal)
|
||||||
|
|
||||||
|
fmt.Println("the quoteAmountStr is:", quoteAmountStr)
|
||||||
|
qouteAmount, err := strconv.ParseFloat(quoteAmountStr, 64)
|
||||||
|
if err != nil {
|
||||||
|
logg.ErrorCtxf(ctx, "failed to parse quoteAmountStr as float", "value", quoteAmountStr, "error", err)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format to 2 decimal places
|
||||||
|
qouteStr := fmt.Sprintf("%.2f", qouteAmount)
|
||||||
|
|
||||||
|
res.Content = fmt.Sprintf(
|
||||||
|
"%s will receive %s %s",
|
||||||
|
string(recipientPhoneNumber), qouteStr, swapData.ActiveSwapToSym,
|
||||||
|
)
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransactionInitiateSwap calls the poolSwap and returns a confirmation based on the result.
|
||||||
|
func (h *MenuHandlers) TransactionInitiateSwap(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_swap_transaction, _ := h.flagManager.GetFlag("flag_swap_transaction")
|
||||||
|
|
||||||
|
code := codeFromCtx(ctx)
|
||||||
|
l := gotext.NewLocale(translationDir, code)
|
||||||
|
l.AddDomain("default")
|
||||||
|
|
||||||
|
userStore := h.userdataStore
|
||||||
|
|
||||||
|
swapData, err := store.ReadSwapPreviewData(ctx, userStore, sessionId)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
swapAmount, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SWAP_AMOUNT)
|
||||||
|
if err != nil {
|
||||||
|
logg.ErrorCtxf(ctx, "failed to read swapAmount entry with", "key", storedb.DATA_ACTIVE_SWAP_AMOUNT, "error", err)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
swapAmountStr := string(swapAmount)
|
||||||
|
|
||||||
|
// Call the poolSwap API
|
||||||
|
poolSwap, err := h.accountService.PoolSwap(ctx, swapAmountStr, swapData.PublicKey, swapData.ActiveSwapFromAddress, swapData.ActivePoolAddress, swapData.ActiveSwapToAddress)
|
||||||
|
if err != nil {
|
||||||
|
flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
|
||||||
|
res.FlagSet = append(res.FlagSet, flag_api_error)
|
||||||
|
res.Content = l.Get("Your request failed. Please try again later.")
|
||||||
|
logg.ErrorCtxf(ctx, "failed on poolSwap", "error", err)
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
swapTrackingId := poolSwap.TrackingId
|
||||||
|
logg.InfoCtxf(ctx, "poolSwap", "swapTrackingId", swapTrackingId)
|
||||||
|
|
||||||
|
// Initiate a send
|
||||||
|
recipientPublicKey, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_RECIPIENT)
|
||||||
|
if err != nil {
|
||||||
|
logg.ErrorCtxf(ctx, "failed to read swapAmount entry with", "key", storedb.DATA_ACTIVE_SWAP_AMOUNT, "error", err)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
recipientPhoneNumber, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_RECIPIENT_PHONE_NUMBER)
|
||||||
|
if err != nil {
|
||||||
|
// invalid state
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call TokenTransfer
|
||||||
|
tokenTransfer, err := h.accountService.TokenTransfer(ctx, swapAmountStr, swapData.PublicKey, string(recipientPublicKey), swapData.ActiveSwapToAddress)
|
||||||
|
if err != nil {
|
||||||
|
flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
|
||||||
|
res.FlagSet = append(res.FlagSet, flag_api_error)
|
||||||
|
res.Content = l.Get("Your request failed. Please try again later.")
|
||||||
|
logg.ErrorCtxf(ctx, "failed on TokenTransfer", "error", err)
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
trackingId := tokenTransfer.TrackingId
|
||||||
|
logg.InfoCtxf(ctx, "TokenTransfer", "trackingId", trackingId)
|
||||||
|
|
||||||
|
res.Content = l.Get(
|
||||||
|
"Your request has been sent. %s will receive %s %s from %s.",
|
||||||
|
string(recipientPhoneNumber),
|
||||||
|
swapData.TemporaryValue,
|
||||||
|
swapData.ActiveSwapToSym,
|
||||||
|
sessionId,
|
||||||
|
)
|
||||||
|
|
||||||
|
res.FlagReset = append(res.FlagReset, flag_account_authorized, flag_swap_transaction)
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -136,6 +136,9 @@ 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.first = appHandlers.Init
|
ls.first = appHandlers.Init
|
||||||
|
|
||||||
return appHandlers, nil
|
return appHandlers, nil
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user