118 lines
5.2 KiB
Go
118 lines
5.2 KiB
Go
package transfer
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
|
|
"github.com/tech/sendico/gateway/chain/internal/service/gateway/driver"
|
|
"github.com/tech/sendico/gateway/chain/internal/service/gateway/shared"
|
|
"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("Empty request received")
|
|
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)
|
|
}
|
|
|
|
walletForFee := sourceWallet
|
|
nativeCurrency := shared.NativeCurrency(networkCfg)
|
|
if nativeCurrency != "" && strings.EqualFold(nativeCurrency, amount.GetCurrency()) {
|
|
copyWallet := *sourceWallet
|
|
copyWallet.ContractAddress = ""
|
|
copyWallet.TokenSymbol = nativeCurrency
|
|
walletForFee = ©Wallet
|
|
}
|
|
|
|
driverDeps := driver.Deps{
|
|
Logger: c.deps.Logger,
|
|
Registry: c.deps.Networks,
|
|
RPCTimeout: c.deps.RPCTimeout,
|
|
}
|
|
feeMoney, err := chainDriver.EstimateFee(ctx, driverDeps, networkCfg, walletForFee, 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(walletForFee.ContractAddress) == "" {
|
|
contextLabel = "native_transfer"
|
|
}
|
|
resp := &chainv1.EstimateTransferFeeResponse{
|
|
NetworkFee: feeMoney,
|
|
EstimationContext: contextLabel,
|
|
}
|
|
return gsresponse.Success(resp)
|
|
}
|