108 lines
4.8 KiB
Go
108 lines
4.8 KiB
Go
package transfer
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
|
|
"github.com/tech/sendico/gateway/chain/internal/service/gateway/driver"
|
|
"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 estimateTransferFeeCommand struct {
|
|
deps Deps
|
|
}
|
|
|
|
func NewEstimateTransfer(deps Deps) *estimateTransferFeeCommand {
|
|
return &estimateTransferFeeCommand{deps: deps}
|
|
}
|
|
|
|
func (c *estimateTransferFeeCommand) Execute(ctx context.Context, req *chainv1.EstimateTransferFeeRequest) gsresponse.Responder[chainv1.EstimateTransferFeeResponse] {
|
|
if err := c.deps.EnsureRepository(ctx); err != nil {
|
|
c.deps.Logger.Warn("repository unavailable", zap.Error(err))
|
|
return gsresponse.Unavailable[chainv1.EstimateTransferFeeResponse](c.deps.Logger, mservice.ChainGateway, err)
|
|
}
|
|
if req == nil {
|
|
c.deps.Logger.Warn("nil request")
|
|
return gsresponse.InvalidArgument[chainv1.EstimateTransferFeeResponse](c.deps.Logger, mservice.ChainGateway, merrors.InvalidArgument("request is required"))
|
|
}
|
|
|
|
sourceWalletRef := strings.TrimSpace(req.GetSourceWalletRef())
|
|
if sourceWalletRef == "" {
|
|
c.deps.Logger.Warn("source wallet ref missing")
|
|
return gsresponse.InvalidArgument[chainv1.EstimateTransferFeeResponse](c.deps.Logger, mservice.ChainGateway, merrors.InvalidArgument("source_wallet_ref is required"))
|
|
}
|
|
amount := req.GetAmount()
|
|
if amount == nil || strings.TrimSpace(amount.GetAmount()) == "" || strings.TrimSpace(amount.GetCurrency()) == "" {
|
|
c.deps.Logger.Warn("amount missing or incomplete")
|
|
return gsresponse.InvalidArgument[chainv1.EstimateTransferFeeResponse](c.deps.Logger, mservice.ChainGateway, merrors.InvalidArgument("amount is required"))
|
|
}
|
|
|
|
sourceWallet, err := c.deps.Storage.Wallets().Get(ctx, sourceWalletRef)
|
|
if err != nil {
|
|
if errors.Is(err, merrors.ErrNoData) {
|
|
c.deps.Logger.Warn("source wallet not found", zap.String("source_wallet_ref", sourceWalletRef))
|
|
return gsresponse.NotFound[chainv1.EstimateTransferFeeResponse](c.deps.Logger, mservice.ChainGateway, err)
|
|
}
|
|
c.deps.Logger.Warn("storage get wallet failed", zap.Error(err), zap.String("source_wallet_ref", sourceWalletRef))
|
|
return gsresponse.Auto[chainv1.EstimateTransferFeeResponse](c.deps.Logger, mservice.ChainGateway, err)
|
|
}
|
|
|
|
networkKey := strings.ToLower(strings.TrimSpace(sourceWallet.Network))
|
|
networkCfg, ok := c.deps.Networks.Network(networkKey)
|
|
if !ok {
|
|
c.deps.Logger.Warn("unsupported chain", zap.String("network", networkKey))
|
|
return gsresponse.InvalidArgument[chainv1.EstimateTransferFeeResponse](c.deps.Logger, mservice.ChainGateway, merrors.InvalidArgument("unsupported chain for wallet"))
|
|
}
|
|
if c.deps.Drivers == nil {
|
|
c.deps.Logger.Warn("chain drivers missing", zap.String("network", networkKey))
|
|
return gsresponse.Internal[chainv1.EstimateTransferFeeResponse](c.deps.Logger, mservice.ChainGateway, merrors.Internal("chain drivers not configured"))
|
|
}
|
|
chainDriver, err := c.deps.Drivers.Driver(networkKey)
|
|
if err != nil {
|
|
c.deps.Logger.Warn("unsupported chain driver", zap.String("network", networkKey), zap.Error(err))
|
|
return gsresponse.InvalidArgument[chainv1.EstimateTransferFeeResponse](c.deps.Logger, mservice.ChainGateway, merrors.InvalidArgument("unsupported chain for wallet"))
|
|
}
|
|
|
|
dest, err := resolveDestination(ctx, c.deps, req.GetDestination(), sourceWallet)
|
|
if err != nil {
|
|
if errors.Is(err, merrors.ErrNoData) {
|
|
c.deps.Logger.Warn("destination not found", zap.String("destination_wallet_ref", req.GetDestination().GetManagedWalletRef()))
|
|
return gsresponse.NotFound[chainv1.EstimateTransferFeeResponse](c.deps.Logger, mservice.ChainGateway, err)
|
|
}
|
|
c.deps.Logger.Warn("invalid destination", zap.Error(err))
|
|
return gsresponse.InvalidArgument[chainv1.EstimateTransferFeeResponse](c.deps.Logger, mservice.ChainGateway, err)
|
|
}
|
|
|
|
destinationAddress, err := destinationAddress(ctx, c.deps, chainDriver, dest)
|
|
if err != nil {
|
|
c.deps.Logger.Warn("failed to resolve destination address", zap.Error(err))
|
|
return gsresponse.InvalidArgument[chainv1.EstimateTransferFeeResponse](c.deps.Logger, mservice.ChainGateway, err)
|
|
}
|
|
|
|
driverDeps := driver.Deps{
|
|
Logger: c.deps.Logger,
|
|
Registry: c.deps.Networks,
|
|
RPCTimeout: c.deps.RPCTimeout,
|
|
}
|
|
feeMoney, err := chainDriver.EstimateFee(ctx, driverDeps, networkCfg, sourceWallet, destinationAddress, amount)
|
|
if err != nil {
|
|
c.deps.Logger.Warn("fee estimation failed", zap.Error(err))
|
|
return gsresponse.Auto[chainv1.EstimateTransferFeeResponse](c.deps.Logger, mservice.ChainGateway, err)
|
|
}
|
|
|
|
contextLabel := "erc20_transfer"
|
|
if strings.TrimSpace(sourceWallet.ContractAddress) == "" {
|
|
contextLabel = "native_transfer"
|
|
}
|
|
resp := &chainv1.EstimateTransferFeeResponse{
|
|
NetworkFee: feeMoney,
|
|
EstimationContext: contextLabel,
|
|
}
|
|
return gsresponse.Success(resp)
|
|
}
|