Files
sendico/api/gateway/chain/storage/model/wallet.go
2025-12-24 13:20:25 +01:00

130 lines
4.1 KiB
Go

package model
import (
"strings"
"time"
"github.com/tech/sendico/pkg/db/storable"
pkgmodel "github.com/tech/sendico/pkg/model"
"github.com/tech/sendico/pkg/mservice"
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
)
type ManagedWalletStatus string
const (
ManagedWalletStatusActive ManagedWalletStatus = "active"
ManagedWalletStatusSuspended ManagedWalletStatus = "suspended"
ManagedWalletStatusClosed ManagedWalletStatus = "closed"
)
// ManagedWallet represents a user-controlled on-chain wallet managed by the service.
type ManagedWallet struct {
storable.Base `bson:",inline" json:",inline"`
pkgmodel.Describable `bson:",inline" json:",inline"`
IdempotencyKey string `bson:"idempotencyKey" json:"idempotencyKey"`
WalletRef string `bson:"walletRef" json:"walletRef"`
OrganizationRef string `bson:"organizationRef" json:"organizationRef"`
OwnerRef string `bson:"ownerRef" json:"ownerRef"`
Network string `bson:"network" json:"network"`
TokenSymbol string `bson:"tokenSymbol" json:"tokenSymbol"`
ContractAddress string `bson:"contractAddress" json:"contractAddress"`
DepositAddress string `bson:"depositAddress" json:"depositAddress"`
KeyReference string `bson:"keyReference,omitempty" json:"keyReference,omitempty"`
Status ManagedWalletStatus `bson:"status" json:"status"`
Metadata map[string]string `bson:"metadata,omitempty" json:"metadata,omitempty"`
}
// Collection implements storable.Storable.
func (*ManagedWallet) Collection() string {
return mservice.ChainWallets
}
// WalletBalance captures computed wallet balances.
type WalletBalance struct {
storable.Base `bson:",inline" json:",inline"`
WalletRef string `bson:"walletRef" json:"walletRef"`
Available *moneyv1.Money `bson:"available" json:"available"`
PendingInbound *moneyv1.Money `bson:"pendingInbound,omitempty" json:"pendingInbound,omitempty"`
PendingOutbound *moneyv1.Money `bson:"pendingOutbound,omitempty" json:"pendingOutbound,omitempty"`
CalculatedAt time.Time `bson:"calculatedAt" json:"calculatedAt"`
}
// Collection implements storable.Storable.
func (*WalletBalance) Collection() string {
return mservice.ChainWalletBalances
}
// ManagedWalletFilter describes list filters.
type ManagedWalletFilter struct {
OrganizationRef string
OwnerRef string
Network string
TokenSymbol string
Cursor string
Limit int32
}
// ManagedWalletList contains paginated wallet results.
type ManagedWalletList struct {
Items []*ManagedWallet
NextCursor string
}
// Normalize trims string fields for consistent indexing.
func (m *ManagedWallet) Normalize() {
m.IdempotencyKey = strings.TrimSpace(m.IdempotencyKey)
m.WalletRef = strings.TrimSpace(m.WalletRef)
m.OrganizationRef = strings.TrimSpace(m.OrganizationRef)
m.OwnerRef = strings.TrimSpace(m.OwnerRef)
m.Name = strings.TrimSpace(m.Name)
if m.Description != nil {
desc := strings.TrimSpace(*m.Description)
if desc == "" {
m.Description = nil
} else {
m.Description = &desc
}
}
m.Network = strings.TrimSpace(strings.ToLower(m.Network))
m.TokenSymbol = strings.TrimSpace(strings.ToUpper(m.TokenSymbol))
m.ContractAddress = strings.TrimSpace(strings.ToLower(m.ContractAddress))
m.DepositAddress = normalizeWalletAddress(m.DepositAddress)
m.KeyReference = strings.TrimSpace(m.KeyReference)
}
// Normalize trims wallet balance identifiers.
func (b *WalletBalance) Normalize() {
b.WalletRef = strings.TrimSpace(b.WalletRef)
}
func normalizeWalletAddress(address string) string {
trimmed := strings.TrimSpace(address)
if trimmed == "" {
return ""
}
if isHexAddress(trimmed) {
return strings.ToLower(trimmed)
}
return trimmed
}
func isHexAddress(value string) bool {
trimmed := strings.TrimPrefix(strings.TrimSpace(value), "0x")
if len(trimmed) != 40 && len(trimmed) != 42 {
return false
}
for _, r := range trimmed {
switch {
case r >= '0' && r <= '9':
case r >= 'a' && r <= 'f':
case r >= 'A' && r <= 'F':
default:
return false
}
}
return true
}