All checks were successful
ci/woodpecker/push/billing_fees Pipeline was successful
ci/woodpecker/push/bff Pipeline was successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/chain_gateway Pipeline was successful
ci/woodpecker/push/fx_ingestor Pipeline was successful
ci/woodpecker/push/fx_oracle Pipeline was successful
ci/woodpecker/push/frontend Pipeline was successful
ci/woodpecker/push/nats Pipeline was successful
ci/woodpecker/push/ledger Pipeline was successful
ci/woodpecker/push/notification Pipeline was successful
ci/woodpecker/push/payments_orchestrator Pipeline was successful
124 lines
5.6 KiB
Go
124 lines
5.6 KiB
Go
package wallet
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
|
|
"github.com/tech/sendico/gateway/chain/internal/service/gateway/shared"
|
|
"github.com/tech/sendico/gateway/chain/storage/model"
|
|
"github.com/tech/sendico/pkg/api/routers/gsresponse"
|
|
"github.com/tech/sendico/pkg/merrors"
|
|
"github.com/tech/sendico/pkg/mservice"
|
|
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type createManagedWalletCommand struct {
|
|
deps Deps
|
|
}
|
|
|
|
func NewCreateManagedWallet(deps Deps) *createManagedWalletCommand {
|
|
return &createManagedWalletCommand{deps: deps}
|
|
}
|
|
|
|
func (c *createManagedWalletCommand) Execute(ctx context.Context, req *chainv1.CreateManagedWalletRequest) gsresponse.Responder[chainv1.CreateManagedWalletResponse] {
|
|
if err := c.deps.EnsureRepository(ctx); err != nil {
|
|
c.deps.Logger.Warn("repository unavailable", zap.Error(err))
|
|
return gsresponse.Unavailable[chainv1.CreateManagedWalletResponse](c.deps.Logger, mservice.ChainGateway, err)
|
|
}
|
|
if req == nil {
|
|
c.deps.Logger.Warn("nil request")
|
|
return gsresponse.InvalidArgument[chainv1.CreateManagedWalletResponse](c.deps.Logger, mservice.ChainGateway, merrors.InvalidArgument("nil request"))
|
|
}
|
|
|
|
idempotencyKey := strings.TrimSpace(req.GetIdempotencyKey())
|
|
if idempotencyKey == "" {
|
|
c.deps.Logger.Warn("missing idempotency key")
|
|
return gsresponse.InvalidArgument[chainv1.CreateManagedWalletResponse](c.deps.Logger, mservice.ChainGateway, merrors.InvalidArgument("idempotency_key is required"))
|
|
}
|
|
organizationRef := strings.TrimSpace(req.GetOrganizationRef())
|
|
if organizationRef == "" {
|
|
c.deps.Logger.Warn("missing organization ref")
|
|
return gsresponse.InvalidArgument[chainv1.CreateManagedWalletResponse](c.deps.Logger, mservice.ChainGateway, merrors.InvalidArgument("organization_ref is required"))
|
|
}
|
|
ownerRef := strings.TrimSpace(req.GetOwnerRef())
|
|
if ownerRef == "" {
|
|
c.deps.Logger.Warn("missing owner ref")
|
|
return gsresponse.InvalidArgument[chainv1.CreateManagedWalletResponse](c.deps.Logger, mservice.ChainGateway, merrors.InvalidArgument("owner_ref is required"))
|
|
}
|
|
|
|
asset := req.GetAsset()
|
|
if asset == nil {
|
|
c.deps.Logger.Warn("missing asset")
|
|
return gsresponse.InvalidArgument[chainv1.CreateManagedWalletResponse](c.deps.Logger, mservice.ChainGateway, merrors.InvalidArgument("asset is required"))
|
|
}
|
|
|
|
chainKey, _ := shared.ChainKeyFromEnum(asset.GetChain())
|
|
if chainKey == "" {
|
|
c.deps.Logger.Warn("unsupported chain", zap.Any("chain", asset.GetChain()))
|
|
return gsresponse.InvalidArgument[chainv1.CreateManagedWalletResponse](c.deps.Logger, mservice.ChainGateway, merrors.InvalidArgument("unsupported chain"))
|
|
}
|
|
networkCfg, ok := c.deps.Networks[chainKey]
|
|
if !ok {
|
|
c.deps.Logger.Warn("unsupported chain in config", zap.String("chain", chainKey))
|
|
return gsresponse.InvalidArgument[chainv1.CreateManagedWalletResponse](c.deps.Logger, mservice.ChainGateway, merrors.InvalidArgument("unsupported chain"))
|
|
}
|
|
|
|
tokenSymbol := strings.ToUpper(strings.TrimSpace(asset.GetTokenSymbol()))
|
|
if tokenSymbol == "" {
|
|
c.deps.Logger.Warn("missing token symbol")
|
|
return gsresponse.InvalidArgument[chainv1.CreateManagedWalletResponse](c.deps.Logger, mservice.ChainGateway, merrors.InvalidArgument("asset.token_symbol is required"))
|
|
}
|
|
contractAddress := strings.ToLower(strings.TrimSpace(asset.GetContractAddress()))
|
|
if contractAddress == "" {
|
|
contractAddress = shared.ResolveContractAddress(networkCfg.TokenConfigs, tokenSymbol)
|
|
if contractAddress == "" {
|
|
c.deps.Logger.Warn("unsupported token", zap.String("token", tokenSymbol), zap.String("chain", chainKey))
|
|
return gsresponse.InvalidArgument[chainv1.CreateManagedWalletResponse](c.deps.Logger, mservice.ChainGateway, merrors.InvalidArgument("unsupported token for chain"))
|
|
}
|
|
}
|
|
|
|
walletRef := shared.GenerateWalletRef()
|
|
if c.deps.KeyManager == nil {
|
|
c.deps.Logger.Warn("key manager missing")
|
|
return gsresponse.Internal[chainv1.CreateManagedWalletResponse](c.deps.Logger, mservice.ChainGateway, merrors.Internal("key manager not configured"))
|
|
}
|
|
|
|
keyInfo, err := c.deps.KeyManager.CreateManagedWalletKey(ctx, walletRef, chainKey)
|
|
if err != nil {
|
|
c.deps.Logger.Warn("key manager error", zap.Error(err))
|
|
return gsresponse.Auto[chainv1.CreateManagedWalletResponse](c.deps.Logger, mservice.ChainGateway, err)
|
|
}
|
|
if keyInfo == nil || strings.TrimSpace(keyInfo.Address) == "" {
|
|
c.deps.Logger.Warn("key manager returned empty address")
|
|
return gsresponse.Internal[chainv1.CreateManagedWalletResponse](c.deps.Logger, mservice.ChainGateway, merrors.Internal("key manager returned empty address"))
|
|
}
|
|
|
|
wallet := &model.ManagedWallet{
|
|
IdempotencyKey: idempotencyKey,
|
|
WalletRef: walletRef,
|
|
OrganizationRef: organizationRef,
|
|
OwnerRef: ownerRef,
|
|
Network: chainKey,
|
|
TokenSymbol: tokenSymbol,
|
|
ContractAddress: contractAddress,
|
|
DepositAddress: strings.ToLower(keyInfo.Address),
|
|
KeyReference: keyInfo.KeyID,
|
|
Status: model.ManagedWalletStatusActive,
|
|
Metadata: shared.CloneMetadata(req.GetMetadata()),
|
|
}
|
|
|
|
created, err := c.deps.Storage.Wallets().Create(ctx, wallet)
|
|
if err != nil {
|
|
if errors.Is(err, merrors.ErrDataConflict) {
|
|
c.deps.Logger.Debug("wallet already exists", zap.String("wallet_ref", walletRef), zap.String("idempotency_key", idempotencyKey))
|
|
return gsresponse.Success(&chainv1.CreateManagedWalletResponse{Wallet: toProtoManagedWallet(created)})
|
|
}
|
|
c.deps.Logger.Warn("storage create failed", zap.Error(err), zap.String("wallet_ref", walletRef))
|
|
return gsresponse.Auto[chainv1.CreateManagedWalletResponse](c.deps.Logger, mservice.ChainGateway, err)
|
|
}
|
|
|
|
return gsresponse.Success(&chainv1.CreateManagedWalletResponse{Wallet: toProtoManagedWallet(created)})
|
|
}
|