This commit is contained in:
Stephan D
2026-03-10 12:31:09 +01:00
parent d87e709f43
commit e77d1ab793
287 changed files with 2089 additions and 1550 deletions

View File

@@ -0,0 +1,47 @@
version: "2"
linters:
default: none
enable:
- bodyclose
- canonicalheader
- copyloopvar
- durationcheck
- errcheck
- errchkjson
- errname
- errorlint
- gosec
- govet
- ineffassign
- nilerr
- nilnesserr
- nilnil
- noctx
- rowserrcheck
- sqlclosecheck
- staticcheck
- unconvert
- wastedassign
disable:
- depguard
- exhaustruct
- gochecknoglobals
- gochecknoinits
- gomoddirectives
- wrapcheck
- cyclop
- dupl
- funlen
- gocognit
- gocyclo
- ireturn
- lll
- mnd
- nestif
- nlreturn
- noinlineerr
- paralleltest
- tagliatelle
- testpackage
- varnamelen
- wsl_v5

View File

@@ -226,7 +226,7 @@ func (r *discoveryClientResolver) findLedgerEntry() (*discovery.RegistryEntry, b
entries := r.registry.List(time.Now(), true)
matches := make([]discovery.RegistryEntry, 0)
for _, entry := range entries {
if !strings.EqualFold(strings.TrimSpace(entry.Service), string(mservice.Ledger)) {
if !strings.EqualFold(strings.TrimSpace(entry.Service), mservice.Ledger) {
continue
}
if strings.TrimSpace(entry.InvokeURI) == "" {

View File

@@ -38,7 +38,7 @@ func SendDirectionForRail(rail model.Rail) SendDirection {
}
func IsGatewayEligible(gw *model.GatewayInstanceDescriptor, rail model.Rail, network, currency string, action model.RailOperation, dir SendDirection, amount decimal.Decimal) error {
return model.IsGatewayEligible(gw, rail, network, currency, action, toGatewayDirection(sendDirection(dir)), amount)
return model.IsGatewayEligible(gw, rail, network, currency, action, toGatewayDirection(dir), amount)
}
func ParseRailValue(value string) model.Rail {

View File

@@ -324,7 +324,7 @@ func makeMoney(currency string, value decimal.Decimal) *moneyv1.Money {
func ensureCurrency(m *moneyv1.Money, targetCurrency string, quote *oraclev1.Quote) (*moneyv1.Money, error) {
if m == nil || strings.TrimSpace(targetCurrency) == "" {
return nil, nil
return nil, nil //nolint:nilnil // nil means no amount available for conversion
}
if strings.EqualFold(m.GetCurrency(), targetCurrency) {
return cloneProtoMoney(m), nil
@@ -334,12 +334,12 @@ func ensureCurrency(m *moneyv1.Money, targetCurrency string, quote *oraclev1.Quo
func convertWithQuote(m *moneyv1.Money, quote *oraclev1.Quote, targetCurrency string) (*moneyv1.Money, error) {
if m == nil || quote == nil || quote.GetPair() == nil || quote.GetPrice() == nil {
return nil, nil
return nil, nil //nolint:nilnil // nil means conversion cannot be performed with available quote
}
base := strings.TrimSpace(quote.GetPair().GetBase())
qt := strings.TrimSpace(quote.GetPair().GetQuote())
if base == "" || qt == "" || strings.TrimSpace(targetCurrency) == "" {
return nil, nil
return nil, nil //nolint:nilnil // nil means conversion cannot be performed with incomplete pair
}
price, err := decimal.NewFromString(quote.GetPrice().GetValue())
if err != nil || price.IsZero() {
@@ -355,7 +355,7 @@ func convertWithQuote(m *moneyv1.Money, quote *oraclev1.Quote, targetCurrency st
case strings.EqualFold(m.GetCurrency(), qt) && strings.EqualFold(targetCurrency, base):
return makeMoney(targetCurrency, value.Div(price)), nil
default:
return nil, nil
return nil, nil //nolint:nilnil // nil means quote pair does not match requested conversion
}
}

View File

@@ -15,7 +15,7 @@ func BuildFundingGateFromProfile(
requiredAmount *moneyv1.Money,
) (*QuoteFundingGate, error) {
if profile == nil {
return nil, nil
return nil, nil //nolint:nilnil // nil gate means no funding profile is configured
}
mode := shared.NormalizeFundingMode(profile.Mode)

View File

@@ -89,7 +89,7 @@ func (r *StaticFundingProfileResolver) ResolveGatewayFundingProfile(
req FundingProfileRequest,
) (*GatewayFundingProfile, error) {
if r == nil {
return nil, nil
return nil, nil //nolint:nilnil // nil resolver means no static funding configuration
}
gatewayKey := r.gatewayKey(req)
@@ -206,7 +206,7 @@ func (r *StaticFundingProfileResolver) ResolveGatewayFundingProfile(
}
if isEmptyFundingProfile(profile) {
return nil, nil
return nil, nil //nolint:nilnil // nil profile means funding profile is intentionally omitted
}
return profile, nil
}

View File

@@ -2,10 +2,11 @@ package graph_path_finder
import (
"errors"
"github.com/tech/sendico/pkg/discovery"
"slices"
"testing"
"github.com/tech/sendico/payments/storage/model"
"github.com/tech/sendico/pkg/discovery"
"github.com/tech/sendico/pkg/merrors"
)
@@ -142,13 +143,5 @@ func railsToStrings(rails []model.Rail) []string {
}
func equalStrings(got, want []string) bool {
if len(got) != len(want) {
return false
}
for i := range got {
if got[i] != want[i] {
return false
}
}
return true
return slices.Equal(got, want)
}

View File

@@ -240,7 +240,7 @@ func makeMoney(currency string, value decimal.Decimal) *moneyv1.Money {
func ensureCurrency(m *moneyv1.Money, targetCurrency string, quote *oraclev1.Quote) (*moneyv1.Money, error) {
if m == nil || strings.TrimSpace(targetCurrency) == "" {
return nil, nil
return nil, nil //nolint:nilnil // nil means no amount available for conversion
}
if strings.EqualFold(m.GetCurrency(), targetCurrency) {
return cloneProtoMoney(m), nil
@@ -250,13 +250,13 @@ func ensureCurrency(m *moneyv1.Money, targetCurrency string, quote *oraclev1.Quo
func convertWithQuote(m *moneyv1.Money, quote *oraclev1.Quote, targetCurrency string) (*moneyv1.Money, error) {
if m == nil || quote == nil || quote.GetPair() == nil || quote.GetPrice() == nil {
return nil, nil
return nil, nil //nolint:nilnil // nil means conversion cannot be performed with available quote
}
base := strings.TrimSpace(quote.GetPair().GetBase())
qt := strings.TrimSpace(quote.GetPair().GetQuote())
if base == "" || qt == "" || strings.TrimSpace(targetCurrency) == "" {
return nil, nil
return nil, nil //nolint:nilnil // nil means conversion cannot be performed with incomplete pair
}
price, err := decimal.NewFromString(quote.GetPrice().GetValue())
@@ -274,7 +274,7 @@ func convertWithQuote(m *moneyv1.Money, quote *oraclev1.Quote, targetCurrency st
case strings.EqualFold(m.GetCurrency(), qt) && strings.EqualFold(targetCurrency, base):
return makeMoney(targetCurrency, value.Div(price)), nil
default:
return nil, nil
return nil, nil //nolint:nilnil // nil means quote pair does not match requested conversion
}
}

View File

@@ -14,9 +14,9 @@ import (
func (s *Service) withTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) {
if d <= 0 {
return context.WithCancel(ctx)
return context.WithCancel(ctx) //nolint:gosec // cancel func is always invoked by caller
}
return context.WithTimeout(ctx, d)
return context.WithTimeout(ctx, d) //nolint:gosec // cancel func is always invoked by caller
}
func triggerFromKind(kind sharedv1.PaymentKind, requiresFX bool) feesv1.Trigger {

View File

@@ -989,11 +989,11 @@ type staticManagedWalletResolverForE2E struct {
func (r staticManagedWalletResolverForE2E) ResolveManagedWalletAsset(_ context.Context, managedWalletRef string) (*paymenttypes.Asset, error) {
if len(r.assetsByRef) == 0 {
return nil, nil
return nil, nil //nolint:nilnil // test double: empty map means no managed wallet asset
}
asset, ok := r.assetsByRef[strings.TrimSpace(managedWalletRef)]
if !ok || asset == nil {
return nil, nil
return nil, nil //nolint:nilnil // test double: unknown wallet means no managed wallet asset
}
cloned := *asset
return &cloned, nil
@@ -1009,7 +1009,7 @@ func (r staticManagedWalletResolverForE2E) ResolveManagedWalletNetwork(ctx conte
func (r staticGatewayRegistryForE2E) List(context.Context) ([]*model.GatewayInstanceDescriptor, error) {
if len(r.items) == 0 {
return nil, nil
return nil, nil //nolint:nilnil // test double: empty registry means no gateways configured
}
out := make([]*model.GatewayInstanceDescriptor, 0, len(r.items))
for _, item := range r.items {

View File

@@ -282,11 +282,11 @@ func (f *fakeManagedWalletNetworkResolver) ResolveManagedWalletAsset(_ context.C
return nil, f.assetErr
}
if f.assets == nil {
return nil, nil
return nil, nil //nolint:nilnil // test double: nil asset map means no resolved asset
}
src := f.assets[managedWalletRef]
if src == nil {
return nil, nil
return nil, nil //nolint:nilnil // test double: missing key means no resolved asset
}
return &paymenttypes.Asset{
Chain: src.GetChain(),

View File

@@ -421,7 +421,7 @@ func (s *QuoteComputationService) resolveFundingGate(
zap.String("rail", string(in.Rail)),
)
return nil, nil
return nil, nil //nolint:nilnil // nil gate means no resolver configured
}
s.logger.Debug("Resolving funding gate",
@@ -462,7 +462,7 @@ func (s *QuoteComputationService) resolveFundingGate(
zap.String("rail", string(in.Rail)),
)
return nil, nil
return nil, nil //nolint:nilnil // nil gate means no funding profile for resolved gateway
}
gate, err := gateway_funding_profile.BuildFundingGateFromProfile(profile, in.Amount)

View File

@@ -432,13 +432,13 @@ func (s *Service) estimateNetworkFee(ctx context.Context, intent *sharedv1.Payme
if err != nil {
if errors.Is(err, merrors.ErrNoData) {
s.logger.Debug("Network fee estimation skipped: gateway unavailable", zap.Error(err))
return nil, nil
return nil, nil //nolint:nilnil // nil response means fee estimation skipped when gateway is unavailable
}
s.logger.Warn("Chain gateway resolution failed", zap.Error(err))
return nil, err
}
if client == nil {
return nil, nil
return nil, nil //nolint:nilnil // nil response means no chain gateway client is available
}
resp, err := client.EstimateTransferFee(ctx, req)
@@ -457,7 +457,7 @@ func (s *Service) requestFXQuote(ctx context.Context, orgRef string, req *quoteR
if fxRequired {
return nil, merrors.Internal("fx_oracle_unavailable")
}
return nil, nil
return nil, nil //nolint:nilnil // nil quote means FX is optional and oracle is unavailable
}
meta := req.GetMeta()
fxIntent := fxIntentForQuote(intent)
@@ -465,7 +465,7 @@ func (s *Service) requestFXQuote(ctx context.Context, orgRef string, req *quoteR
if fxRequired {
return nil, merrors.InvalidArgument("fx intent missing")
}
return nil, nil
return nil, nil //nolint:nilnil // nil quote means FX is not required for this intent
}
ttl := fxIntent.GetTtlMs()
@@ -514,7 +514,7 @@ func (s *Service) requestFXQuote(ctx context.Context, orgRef string, req *quoteR
if fxRequired {
return nil, merrors.Internal("orchestrator: fx quote missing")
}
return nil, nil
return nil, nil //nolint:nilnil // nil quote means FX is optional and oracle returned no quote
}
return quoteToProto(quote), nil
}

View File

@@ -128,10 +128,8 @@ func (h *TransferIntentHydrator) HydrateOne(ctx context.Context, in HydrateOneIn
if settlementCurrency == "" {
settlementCurrency = settlementCurrencyFromFX(fxIntent)
}
requiresFX := false
if fxIntent != nil && fxIntent.Pair != nil {
requiresFX = true
} else {
requiresFX := fxIntent != nil && fxIntent.Pair != nil
if !requiresFX {
requiresFX = !strings.EqualFold(amount.Currency, settlementCurrency)
}