diff --git a/config/config.go b/config/config.go index 5ac8438..e7ffa97 100644 --- a/config/config.go +++ b/config/config.go @@ -7,25 +7,27 @@ import ( ) const ( - createAccountPath = "/api/v2/account/create" - trackStatusPath = "/api/track" - balancePathPrefix = "/api/account" - trackPath = "/api/v2/account/status" - tokenTransferPrefix = "/api/v2/token/transfer" - voucherHoldingsPathPrefix = "/api/v1/holdings" - voucherTransfersPathPrefix = "/api/v1/transfers/last10" - voucherDataPathPrefix = "/api/v1/token" - SendSMSPrefix = "api/v1/external/upsell" - poolDepositPrefix = "/api/v2/pool/deposit" - poolSwapQoutePrefix = "/api/v2/pool/quote" - poolSwapPrefix = "/api/v2/pool/swap" - topPoolsPrefix = "/api/v1/pool/top" - retrievePoolDetailsPrefix = "/api/v1/pool/reverse" - poolSwappableVouchersPrefix = "/api/v1/pool" - AliasRegistrationPrefix = "/api/v1/internal/register" - AliasResolverPrefix = "/api/v1/resolve" - ExternalSMSPrefix = "/api/v1/external" - AliasUpdatePrefix = "/api/v1/internal/update" + createAccountPath = "/api/v2/account/create" + trackStatusPath = "/api/track" + balancePathPrefix = "/api/account" + trackPath = "/api/v2/account/status" + tokenTransferPrefix = "/api/v2/token/transfer" + voucherHoldingsPathPrefix = "/api/v1/holdings" + voucherTransfersPathPrefix = "/api/v1/transfers/last10" + voucherDataPathPrefix = "/api/v1/token" + SendSMSPrefix = "api/v1/external/upsell" + poolDepositPrefix = "/api/v2/pool/deposit" + poolSwapQoutePrefix = "/api/v2/pool/quote" + poolSwapPrefix = "/api/v2/pool/swap" + topPoolsPrefix = "/api/v1/pool/top" + retrievePoolDetailsPrefix = "/api/v1/pool/reverse" + poolSwappableVouchersPrefix = "/api/v1/pool" + AliasRegistrationPrefix = "/api/v1/internal/register" + AliasResolverPrefix = "/api/v1/resolve" + ExternalSMSPrefix = "/api/v1/external" + AliasUpdatePrefix = "/api/v1/internal/update" + CreditSendPrefix = "/api/v1/credit-send" + CreditSendReverseQuotePrefix = "/api/v1/pool/reverse-quote" ) var ( @@ -38,25 +40,27 @@ var ( ) var ( - CreateAccountURL string - TrackStatusURL string - BalanceURL string - TrackURL string - TokenTransferURL string - VoucherHoldingsURL string - VoucherTransfersURL string - VoucherDataURL string - PoolDepositURL string - PoolSwapQuoteURL string - PoolSwapURL string - TopPoolsURL string - RetrievePoolDetailsURL string - PoolSwappableVouchersURL string - SendSMSURL string - AliasRegistrationURL string - AliasResolverURL string - ExternalSMSURL string - AliasUpdateURL string + CreateAccountURL string + TrackStatusURL string + BalanceURL string + TrackURL string + TokenTransferURL string + VoucherHoldingsURL string + VoucherTransfersURL string + VoucherDataURL string + PoolDepositURL string + PoolSwapQuoteURL string + PoolSwapURL string + TopPoolsURL string + RetrievePoolDetailsURL string + PoolSwappableVouchersURL string + SendSMSURL string + AliasRegistrationURL string + AliasResolverURL string + ExternalSMSURL string + AliasUpdateURL string + CreditSendURL string + CreditSendReverseQuoteURL string ) func setBase() error { @@ -105,6 +109,8 @@ func LoadConfig() error { AliasResolverURL, _ = url.JoinPath(aliasEnsURLBase, AliasResolverPrefix) ExternalSMSURL, _ = url.JoinPath(externalSMSBase, ExternalSMSPrefix) AliasUpdateURL, _ = url.JoinPath(aliasEnsURLBase, AliasUpdatePrefix) + CreditSendURL, _ = url.JoinPath(dataURLBase, CreditSendPrefix) + CreditSendReverseQuoteURL, _ = url.JoinPath(dataURLBase, CreditSendReverseQuotePrefix) return nil } diff --git a/dev/api.go b/dev/api.go index 6000896..97609b5 100644 --- a/dev/api.go +++ b/dev/api.go @@ -587,10 +587,10 @@ func (das *DevAccountService) FetchVouchers(ctx context.Context, publicKey strin //TODO: Iterate over the account acc.Balances object for _, voucher := range das.vouchers { holdings = append(holdings, dataserviceapi.TokenHoldings{ - TokenAddress: voucher.Address, - TokenSymbol: voucher.Symbol, - TokenDecimals: strconv.Itoa(voucher.Decimals), - Balance: strconv.Itoa(int(defaultVoucherBalance)), + TokenAddress: voucher.Address, + TokenSymbol: voucher.Symbol, + TokenDecimals: strconv.Itoa(voucher.Decimals), + Balance: strconv.Itoa(int(defaultVoucherBalance)), }) } @@ -861,10 +861,10 @@ func (das *DevAccountService) GetPoolSwappableFromVouchers(ctx context.Context, } for _, v := range p.Vouchers { swapFromList = append(swapFromList, dataserviceapi.TokenHoldings{ - TokenAddress: v.Address, - TokenSymbol: v.Symbol, - TokenDecimals: string(defaultDecimals), - Balance: fmt.Sprintf("%f", defaultVoucherBalance), + TokenAddress: v.Address, + TokenSymbol: v.Symbol, + TokenDecimals: string(defaultDecimals), + Balance: fmt.Sprintf("%f", defaultVoucherBalance), }) } @@ -907,3 +907,17 @@ func (das *DevAccountService) CheckTokenInPool(ctx context.Context, poolAddress, CanSwapFrom: true, }, nil } + +func (das *DevAccountService) GetCreditSendMaxLimit(ctx context.Context, poolAddress, fromTokenAddress, toTokenAddress, publicKey string) (*models.CreditSendLimitsResult, error) { + return &models.CreditSendLimitsResult{ + MaxRAT: "45599996", + MaxSAT: "3507692", + }, nil +} + +func (das *DevAccountService) GetCreditSendReverseQuote(ctx context.Context, poolAddress, fromTokenAddress, toTokenAddress, toTokenAMount string) (*models.CreditSendReverseQouteResult, error) { + return &models.CreditSendReverseQouteResult{ + InputAmount: "3076923", + OutputAmount: "40000000", + }, nil +} diff --git a/models/pool_swap_response.go b/models/pool_swap_response.go index 4f2c088..fae2255 100644 --- a/models/pool_swap_response.go +++ b/models/pool_swap_response.go @@ -20,3 +20,13 @@ type MaxLimitResult struct { type TokenInPoolResult struct { CanSwapFrom bool `json:"canSwapFrom"` } + +type CreditSendLimitsResult struct { + MaxRAT string `json:"maxRAT"` + MaxSAT string `json:"maxSAT"` +} + +type CreditSendReverseQouteResult struct { + InputAmount string `json:"inputAmount"` + OutputAmount string `json:"outputAmount"` +} diff --git a/remote/account_service.go b/remote/account_service.go index c33fec3..29286ba 100644 --- a/remote/account_service.go +++ b/remote/account_service.go @@ -30,4 +30,6 @@ type AccountService interface { PoolSwap(ctx context.Context, amount, from, fromTokenAddress, poolAddress, toTokenAddress string) (*models.PoolSwapResult, error) GetSwapFromTokenMaxLimit(ctx context.Context, poolAddress, fromTokenAddress, toTokenAddress, publicKey string) (*models.MaxLimitResult, error) CheckTokenInPool(ctx context.Context, poolAddress, tokenAddress string) (*models.TokenInPoolResult, error) + GetCreditSendMaxLimit(ctx context.Context, poolAddress, fromTokenAddress, toTokenAddress, publicKey string) (*models.CreditSendLimitsResult, error) + GetCreditSendReverseQuote(ctx context.Context, poolAddress, fromTokenAddress, toTokenAddress, toTokenAMount string) (*models.CreditSendReverseQouteResult, error) } diff --git a/remote/http/service.go b/remote/http/service.go index f1f8d55..a3c3e82 100644 --- a/remote/http/service.go +++ b/remote/http/service.go @@ -517,10 +517,10 @@ func (as *HTTPAccountService) GetSwapFromTokenMaxLimit(ctx context.Context, pool } } -func (as *HTTPAccountService) getSwapFromTokenMaxLimit(ctx context.Context, poolAddress, fromTokenAddress, toTokeAddress, publicKey string) (*models.MaxLimitResult, error) { +func (as *HTTPAccountService) getSwapFromTokenMaxLimit(ctx context.Context, poolAddress, fromTokenAddress, toTokenAddress, publicKey string) (*models.MaxLimitResult, error) { var r models.MaxLimitResult - ep, err := url.JoinPath(config.PoolSwappableVouchersURL, poolAddress, "limit", fromTokenAddress, toTokeAddress, publicKey) + ep, err := url.JoinPath(config.PoolSwappableVouchersURL, poolAddress, "limit", fromTokenAddress, toTokenAddress, publicKey) if err != nil { return nil, err } @@ -742,6 +742,46 @@ func (as *HTTPAccountService) SendPINResetSMS(ctx context.Context, admin, phone return nil } +// GetCreditSendMaxLimit calls the API to check credit limits and return the maxRAT and maxSAT +func (as *HTTPAccountService) GetCreditSendMaxLimit(ctx context.Context, poolAddress, fromTokenAddress, toTokenAddress, publicKey string) (*models.CreditSendLimitsResult, error) { + var r models.CreditSendLimitsResult + + ep, err := url.JoinPath(config.CreditSendURL, poolAddress, fromTokenAddress, toTokenAddress, publicKey) + if err != nil { + return nil, err + } + req, err := http.NewRequest("GET", ep, nil) + if err != nil { + return nil, err + } + _, err = doRequest(ctx, req, &r) + if err != nil { + return nil, err + } + + return &r, nil +} + +// GetCreditSendReverseQuote calls the API to getthe reverse quote for sending RAT amount +func (as *HTTPAccountService) GetCreditSendReverseQuote(ctx context.Context, poolAddress, fromTokenAddress, toTokenAddress, toTokenAMount string) (*models.CreditSendReverseQouteResult, error) { + var r models.CreditSendReverseQouteResult + + ep, err := url.JoinPath(config.CreditSendReverseQuoteURL, poolAddress, fromTokenAddress, toTokenAddress, toTokenAMount) + if err != nil { + return nil, err + } + req, err := http.NewRequest("GET", ep, nil) + if err != nil { + return nil, err + } + _, err = doRequest(ctx, req, &r) + if err != nil { + return nil, err + } + + return &r, nil +} + // TODO: remove eth-custodial api dependency func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { var okResponse api.OKResponse diff --git a/testutil/mocks/service_mock.go b/testutil/mocks/service_mock.go index 09ab50b..6962bfb 100644 --- a/testutil/mocks/service_mock.go +++ b/testutil/mocks/service_mock.go @@ -120,3 +120,13 @@ func (m MockAccountService) CheckTokenInPool(ctx context.Context, poolAddress, t args := m.Called(poolAddress, tokenAddress) return args.Get(0).(*models.TokenInPoolResult), args.Error(1) } + +func (m MockAccountService) GetCreditSendMaxLimit(ctx context.Context, poolAddress, fromTokenAddress, toTokenAddress, publicKey string) (*models.CreditSendLimitsResult, error) { + args := m.Called(poolAddress, fromTokenAddress, toTokenAddress, publicKey) + return args.Get(0).(*models.CreditSendLimitsResult), args.Error(1) +} + +func (m MockAccountService) GetCreditSendReverseQuote(ctx context.Context, poolAddress, fromTokenAddress, toTokenAddress, toTokenAMount string) (*models.CreditSendReverseQouteResult, error) { + args := m.Called(poolAddress, fromTokenAddress, toTokenAddress, toTokenAMount) + return args.Get(0).(*models.CreditSendReverseQouteResult), args.Error(1) +} diff --git a/testutil/testservice/account_service.go b/testutil/testservice/account_service.go index 4294b6b..7c0f320 100644 --- a/testutil/testservice/account_service.go +++ b/testutil/testservice/account_service.go @@ -8,6 +8,8 @@ import ( dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) +// This is used in the menu traversal tests + type TestAccountService struct { } @@ -34,12 +36,18 @@ func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { return []dataserviceapi.TokenHoldings{ - dataserviceapi.TokenHoldings{ + { TokenAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "2745987", }, + { + TokenAddress: "0x3f195a3F68BF4c6D49748eFa033a00C6634fF311", + TokenSymbol: "USD", + TokenDecimals: "6", + Balance: "4269100", + }, }, nil } @@ -116,3 +124,11 @@ func (m TestAccountService) GetSwapFromTokenMaxLimit(ctx context.Context, poolAd func (m TestAccountService) CheckTokenInPool(ctx context.Context, poolAddress, tokenAddress string) (*models.TokenInPoolResult, error) { return &models.TokenInPoolResult{}, nil } + +func (m TestAccountService) GetCreditSendMaxLimit(ctx context.Context, poolAddress, fromTokenAddress, toTokenAddress, publicKey string) (*models.CreditSendLimitsResult, error) { + return &models.CreditSendLimitsResult{}, nil +} + +func (m TestAccountService) GetCreditSendReverseQuote(ctx context.Context, poolAddress, fromTokenAddress, toTokenAddress, toTokenAMount string) (*models.CreditSendReverseQouteResult, error) { + return &models.CreditSendReverseQouteResult{}, nil +}