293 lines
8.5 KiB
Go
293 lines
8.5 KiB
Go
package sresponse
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/tech/sendico/pkg/api/http/response"
|
|
"github.com/tech/sendico/pkg/mlogger"
|
|
paymenttypes "github.com/tech/sendico/pkg/payments/types"
|
|
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
|
paginationv1 "github.com/tech/sendico/pkg/proto/common/pagination/v1"
|
|
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
|
|
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
)
|
|
|
|
type walletAsset struct {
|
|
Chain string `json:"chain"`
|
|
TokenSymbol string `json:"tokenSymbol"`
|
|
ContractAddress string `json:"contractAddress"`
|
|
}
|
|
|
|
type wallet struct {
|
|
WalletRef string `json:"walletRef"`
|
|
OrganizationRef string `json:"organizationRef"`
|
|
OwnerRef string `json:"ownerRef"`
|
|
Asset walletAsset `json:"asset"`
|
|
DepositAddress string `json:"depositAddress"`
|
|
Status string `json:"status"`
|
|
Metadata map[string]string `json:"metadata,omitempty"`
|
|
Name string `json:"name"`
|
|
Description *string `json:"description,omitempty"`
|
|
CreatedAt string `json:"createdAt,omitempty"`
|
|
UpdatedAt string `json:"updatedAt,omitempty"`
|
|
}
|
|
|
|
type walletsResponse struct {
|
|
authResponse `json:",inline"`
|
|
Wallets []wallet `json:"wallets"`
|
|
Page *paginationv1.CursorPageResponse `json:"page,omitempty"`
|
|
}
|
|
|
|
type walletBalance struct {
|
|
Available *paymenttypes.Money `json:"available,omitempty"`
|
|
PendingInbound *paymenttypes.Money `json:"pendingInbound,omitempty"`
|
|
PendingOutbound *paymenttypes.Money `json:"pendingOutbound,omitempty"`
|
|
CalculatedAt string `json:"calculatedAt,omitempty"`
|
|
}
|
|
|
|
type walletBalanceResponse struct {
|
|
authResponse `json:",inline"`
|
|
Balance walletBalance `json:"balance"`
|
|
}
|
|
|
|
func Wallets(logger mlogger.Logger, resp *chainv1.ListManagedWalletsResponse, accessToken *TokenData) http.HandlerFunc {
|
|
dto := walletsResponse{
|
|
Page: resp.GetPage(),
|
|
authResponse: authResponse{AccessToken: *accessToken},
|
|
}
|
|
dto.Wallets = make([]wallet, 0, len(resp.GetWallets()))
|
|
for _, w := range resp.GetWallets() {
|
|
dto.Wallets = append(dto.Wallets, toWallet(w))
|
|
}
|
|
return response.Ok(logger, dto)
|
|
}
|
|
|
|
func WalletBalance(logger mlogger.Logger, bal *chainv1.WalletBalance, accessToken *TokenData) http.HandlerFunc {
|
|
return response.Ok(logger, walletBalanceResponse{
|
|
Balance: toWalletBalance(bal),
|
|
authResponse: authResponse{AccessToken: *accessToken},
|
|
})
|
|
}
|
|
|
|
func toWallet(w *chainv1.ManagedWallet) wallet {
|
|
if w == nil {
|
|
return wallet{}
|
|
}
|
|
asset := w.GetAsset()
|
|
chain := ""
|
|
token := ""
|
|
contract := ""
|
|
if asset != nil {
|
|
chain = chainNetworkValue(asset.GetChain())
|
|
token = asset.GetTokenSymbol()
|
|
contract = asset.GetContractAddress()
|
|
}
|
|
name := ""
|
|
if d := w.GetDescribable(); d != nil {
|
|
name = strings.TrimSpace(d.GetName())
|
|
}
|
|
if name == "" {
|
|
name = strings.TrimSpace(w.GetMetadata()["name"])
|
|
}
|
|
if name == "" {
|
|
name = w.GetWalletRef()
|
|
}
|
|
var description *string
|
|
if d := w.GetDescribable(); d != nil && d.Description != nil {
|
|
if trimmed := strings.TrimSpace(d.GetDescription()); trimmed != "" {
|
|
description = &trimmed
|
|
}
|
|
}
|
|
if description == nil {
|
|
if trimmed := strings.TrimSpace(w.GetMetadata()["description"]); trimmed != "" {
|
|
description = &trimmed
|
|
}
|
|
}
|
|
return wallet{
|
|
WalletRef: w.GetWalletRef(),
|
|
OrganizationRef: w.GetOrganizationRef(),
|
|
OwnerRef: w.GetOwnerRef(),
|
|
Asset: walletAsset{
|
|
Chain: chain,
|
|
TokenSymbol: token,
|
|
ContractAddress: contract,
|
|
},
|
|
DepositAddress: w.GetDepositAddress(),
|
|
Status: w.GetStatus().String(),
|
|
Metadata: w.GetMetadata(),
|
|
Name: name,
|
|
Description: description,
|
|
CreatedAt: tsToString(w.GetCreatedAt()),
|
|
UpdatedAt: tsToString(w.GetUpdatedAt()),
|
|
}
|
|
}
|
|
|
|
func toWalletBalance(b *chainv1.WalletBalance) walletBalance {
|
|
if b == nil {
|
|
return walletBalance{}
|
|
}
|
|
return walletBalance{
|
|
Available: toMoney(b.GetAvailable()),
|
|
PendingInbound: toMoney(b.GetPendingInbound()),
|
|
PendingOutbound: toMoney(b.GetPendingOutbound()),
|
|
CalculatedAt: tsToString(b.GetCalculatedAt()),
|
|
}
|
|
}
|
|
|
|
func tsToString(ts *timestamppb.Timestamp) string {
|
|
if ts == nil {
|
|
return ""
|
|
}
|
|
return ts.AsTime().UTC().Format(time.RFC3339)
|
|
}
|
|
|
|
func chainNetworkValue(chain chainv1.ChainNetwork) string {
|
|
name := chain.String()
|
|
if !strings.HasPrefix(name, "CHAIN_NETWORK_") {
|
|
return "unspecified"
|
|
}
|
|
trimmed := strings.TrimPrefix(name, "CHAIN_NETWORK_")
|
|
if trimmed == "" {
|
|
return "unspecified"
|
|
}
|
|
return strings.ToLower(trimmed)
|
|
}
|
|
|
|
// WalletsFromAccounts converts connector accounts to wallet response format.
|
|
// Used when querying multiple gateways via discovery.
|
|
func WalletsFromAccounts(logger mlogger.Logger, accounts []*connectorv1.Account, accessToken *TokenData) http.HandlerFunc {
|
|
dto := walletsResponse{
|
|
authResponse: authResponse{AccessToken: *accessToken},
|
|
}
|
|
dto.Wallets = make([]wallet, 0, len(accounts))
|
|
for _, acc := range accounts {
|
|
if acc == nil {
|
|
continue
|
|
}
|
|
dto.Wallets = append(dto.Wallets, accountToWallet(acc))
|
|
}
|
|
return response.Ok(logger, dto)
|
|
}
|
|
|
|
func accountToWallet(acc *connectorv1.Account) wallet {
|
|
if acc == nil {
|
|
return wallet{}
|
|
}
|
|
|
|
// Extract wallet details from provider details
|
|
details := map[string]interface{}{}
|
|
if acc.GetProviderDetails() != nil {
|
|
details = acc.GetProviderDetails().AsMap()
|
|
}
|
|
|
|
walletRef := ""
|
|
if ref := acc.GetRef(); ref != nil {
|
|
walletRef = strings.TrimSpace(ref.GetAccountId())
|
|
}
|
|
if v := stringFromDetails(details, "wallet_ref"); v != "" {
|
|
walletRef = v
|
|
}
|
|
|
|
organizationRef := stringFromDetails(details, "organization_ref")
|
|
ownerRef := strings.TrimSpace(acc.GetOwnerRef())
|
|
if v := stringFromDetails(details, "owner_ref"); v != "" {
|
|
ownerRef = v
|
|
}
|
|
|
|
chain := stringFromDetails(details, "network")
|
|
tokenSymbol := stringFromDetails(details, "token_symbol")
|
|
contractAddress := stringFromDetails(details, "contract_address")
|
|
depositAddress := stringFromDetails(details, "deposit_address")
|
|
|
|
name := ""
|
|
if d := acc.GetDescribable(); d != nil {
|
|
name = strings.TrimSpace(d.GetName())
|
|
}
|
|
if name == "" {
|
|
name = strings.TrimSpace(acc.GetLabel())
|
|
}
|
|
if name == "" {
|
|
name = walletRef
|
|
}
|
|
|
|
var description *string
|
|
if d := acc.GetDescribable(); d != nil && d.Description != nil {
|
|
if trimmed := strings.TrimSpace(d.GetDescription()); trimmed != "" {
|
|
description = &trimmed
|
|
}
|
|
}
|
|
|
|
status := acc.GetState().String()
|
|
// Convert connector state to wallet status format
|
|
switch acc.GetState() {
|
|
case connectorv1.AccountState_ACCOUNT_ACTIVE:
|
|
status = "MANAGED_WALLET_ACTIVE"
|
|
case connectorv1.AccountState_ACCOUNT_SUSPENDED:
|
|
status = "MANAGED_WALLET_SUSPENDED"
|
|
case connectorv1.AccountState_ACCOUNT_CLOSED:
|
|
status = "MANAGED_WALLET_CLOSED"
|
|
}
|
|
|
|
return wallet{
|
|
WalletRef: walletRef,
|
|
OrganizationRef: organizationRef,
|
|
OwnerRef: ownerRef,
|
|
Asset: walletAsset{
|
|
Chain: chain,
|
|
TokenSymbol: tokenSymbol,
|
|
ContractAddress: contractAddress,
|
|
},
|
|
DepositAddress: depositAddress,
|
|
Status: status,
|
|
Name: name,
|
|
Description: description,
|
|
CreatedAt: tsToString(acc.GetCreatedAt()),
|
|
UpdatedAt: tsToString(acc.GetUpdatedAt()),
|
|
}
|
|
}
|
|
|
|
func stringFromDetails(details map[string]interface{}, key string) string {
|
|
if details == nil {
|
|
return ""
|
|
}
|
|
if value, ok := details[key]; ok {
|
|
return strings.TrimSpace(fmt.Sprint(value))
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// WalletBalanceFromConnector converts connector balance to wallet balance response format.
|
|
// Used when querying gateways via discovery.
|
|
func WalletBalanceFromConnector(logger mlogger.Logger, bal *connectorv1.Balance, accessToken *TokenData) http.HandlerFunc {
|
|
return response.Ok(logger, walletBalanceResponse{
|
|
Balance: connectorBalanceToWalletBalance(bal),
|
|
authResponse: authResponse{AccessToken: *accessToken},
|
|
})
|
|
}
|
|
|
|
func connectorBalanceToWalletBalance(b *connectorv1.Balance) walletBalance {
|
|
if b == nil {
|
|
return walletBalance{}
|
|
}
|
|
return walletBalance{
|
|
Available: connectorMoneyToModel(b.GetAvailable()),
|
|
PendingInbound: connectorMoneyToModel(b.GetPendingInbound()),
|
|
PendingOutbound: connectorMoneyToModel(b.GetPendingOutbound()),
|
|
CalculatedAt: tsToString(b.GetCalculatedAt()),
|
|
}
|
|
}
|
|
|
|
func connectorMoneyToModel(m *moneyv1.Money) *paymenttypes.Money {
|
|
if m == nil {
|
|
return nil
|
|
}
|
|
return &paymenttypes.Money{
|
|
Amount: m.GetAmount(),
|
|
Currency: m.GetCurrency(),
|
|
}
|
|
}
|