From 999f0684cb2e2576d182f1024e5255aa7083ab4c Mon Sep 17 00:00:00 2001 From: Stephan D Date: Mon, 8 Dec 2025 19:52:03 +0100 Subject: [PATCH] version bump + CBR fx ingestor --- api/billing/fees/go.mod | 6 +- api/billing/fees/go.sum | 12 +- api/fx/ingestor/config.yml | 12 + api/fx/ingestor/go.mod | 8 +- api/fx/ingestor/go.sum | 12 +- api/fx/ingestor/internal/app/app.go | 4 +- api/fx/ingestor/internal/config/config.go | 22 +- api/fx/ingestor/internal/fmerrors/market.go | 35 -- api/fx/ingestor/internal/ingestor/service.go | 22 +- .../internal/ingestor/service_test.go | 4 +- .../internal/market/binance/connector.go | 16 +- .../ingestor/internal/market/cbr/connector.go | 537 ++++++++++++++++++ .../internal/market/cbr/connector_test.go | 226 ++++++++ .../internal/market/coingecko/connector.go | 26 +- api/fx/ingestor/internal/market/factory.go | 11 +- api/fx/ingestor/internal/metrics/server.go | 6 +- api/fx/ingestor/internal/model/connector.go | 1 + api/fx/oracle/go.mod | 6 +- api/fx/oracle/go.sum | 12 +- api/fx/storage/go.mod | 4 +- api/fx/storage/go.sum | 8 +- api/gateway/chain/go.mod | 8 +- api/gateway/chain/go.sum | 16 +- api/gateway/mntx/go.mod | 6 +- api/gateway/mntx/go.sum | 12 +- api/ledger/go.mod | 6 +- api/ledger/go.sum | 12 +- api/notification/go.mod | 6 +- api/notification/go.sum | 12 +- api/payments/orchestrator/go.mod | 6 +- api/payments/orchestrator/go.sum | 12 +- api/pkg/go.mod | 6 +- api/pkg/go.sum | 12 +- api/server/go.mod | 6 +- api/server/go.sum | 12 +- 35 files changed, 933 insertions(+), 189 deletions(-) delete mode 100644 api/fx/ingestor/internal/fmerrors/market.go create mode 100644 api/fx/ingestor/internal/market/cbr/connector.go create mode 100644 api/fx/ingestor/internal/market/cbr/connector_test.go diff --git a/api/billing/fees/go.mod b/api/billing/fees/go.mod index a72b51c..18ca5d2 100644 --- a/api/billing/fees/go.mod +++ b/api/billing/fees/go.mod @@ -46,9 +46,9 @@ require ( go.yaml.in/yaml/v2 v2.4.3 // indirect golang.org/x/crypto v0.45.0 // indirect golang.org/x/net v0.47.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect google.golang.org/protobuf v1.36.10 ) diff --git a/api/billing/fees/go.sum b/api/billing/fees/go.sum index d63f35a..4d80caf 100644 --- a/api/billing/fees/go.sum +++ b/api/billing/fees/go.sum @@ -187,24 +187,24 @@ golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= diff --git a/api/fx/ingestor/config.yml b/api/fx/ingestor/config.yml index df025dc..9b352e5 100644 --- a/api/fx/ingestor/config.yml +++ b/api/fx/ingestor/config.yml @@ -8,6 +8,9 @@ market: - driver: COINGECKO settings: base_url: "https://api.coingecko.com/api/v3" + - driver: CBR + settings: + base_url: "https://www.cbr.ru" pairs: BINANCE: - base: "USDT" @@ -26,6 +29,15 @@ market: - base: "USDT" quote: "RUB" symbol: "tether:rub" + CBR: + - base: "USD" + quote: "RUB" + symbol: "USD" + provider: "cbr" + - base: "EUR" + quote: "RUB" + symbol: "EUR" + provider: "cbr" metrics: enabled: true diff --git a/api/fx/ingestor/go.mod b/api/fx/ingestor/go.mod index 0de7c77..4307706 100644 --- a/api/fx/ingestor/go.mod +++ b/api/fx/ingestor/go.mod @@ -13,6 +13,7 @@ require ( github.com/tech/sendico/fx/storage v0.0.0 github.com/tech/sendico/pkg v0.1.0 go.uber.org/zap v1.27.1 + golang.org/x/net v0.47.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -45,10 +46,9 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect golang.org/x/crypto v0.45.0 // indirect - golang.org/x/net v0.47.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect google.golang.org/grpc v1.77.0 // indirect google.golang.org/protobuf v1.36.10 // indirect diff --git a/api/fx/ingestor/go.sum b/api/fx/ingestor/go.sum index d63f35a..4d80caf 100644 --- a/api/fx/ingestor/go.sum +++ b/api/fx/ingestor/go.sum @@ -187,24 +187,24 @@ golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= diff --git a/api/fx/ingestor/internal/app/app.go b/api/fx/ingestor/internal/app/app.go index 28ab12d..38f2055 100644 --- a/api/fx/ingestor/internal/app/app.go +++ b/api/fx/ingestor/internal/app/app.go @@ -7,12 +7,12 @@ import ( "github.com/tech/sendico/fx/ingestor/internal/appversion" "github.com/tech/sendico/fx/ingestor/internal/config" - "github.com/tech/sendico/fx/ingestor/internal/fmerrors" "github.com/tech/sendico/fx/ingestor/internal/ingestor" "github.com/tech/sendico/fx/ingestor/internal/metrics" mongostorage "github.com/tech/sendico/fx/storage/mongo" "github.com/tech/sendico/pkg/api/routers/health" "github.com/tech/sendico/pkg/db" + "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/mlogger" "go.uber.org/zap" ) @@ -26,7 +26,7 @@ type App struct { func New(logger mlogger.Logger, cfgPath string) (*App, error) { if logger == nil { - return nil, fmerrors.New("app: logger is nil") + return nil, merrors.InvalidArgument("app: logger is nil") } path := strings.TrimSpace(cfgPath) if path == "" { diff --git a/api/fx/ingestor/internal/config/config.go b/api/fx/ingestor/internal/config/config.go index d01f31c..53394bf 100644 --- a/api/fx/ingestor/internal/config/config.go +++ b/api/fx/ingestor/internal/config/config.go @@ -5,9 +5,9 @@ import ( "strings" "time" - "github.com/tech/sendico/fx/ingestor/internal/fmerrors" mmodel "github.com/tech/sendico/fx/ingestor/internal/model" "github.com/tech/sendico/pkg/db" + "github.com/tech/sendico/pkg/merrors" "gopkg.in/yaml.v3" ) @@ -25,33 +25,33 @@ type Config struct { func Load(path string) (*Config, error) { if path == "" { - return nil, fmerrors.New("config: path is empty") + return nil, merrors.InvalidArgument("config: path is empty") } data, err := os.ReadFile(path) if err != nil { - return nil, fmerrors.Wrap("config: failed to read file", err) + return nil, merrors.InternalWrap(err, "config: failed to read file") } cfg := &Config{} if err := yaml.Unmarshal(data, cfg); err != nil { - return nil, fmerrors.Wrap("config: failed to parse yaml", err) + return nil, merrors.InternalWrap(err, "config: failed to parse yaml") } if len(cfg.Market.Sources) == 0 { - return nil, fmerrors.New("config: no market sources configured") + return nil, merrors.InvalidArgument("config: no market sources configured") } sourceSet := make(map[mmodel.Driver]struct{}, len(cfg.Market.Sources)) for idx := range cfg.Market.Sources { src := &cfg.Market.Sources[idx] if src.Driver.IsEmpty() { - return nil, fmerrors.New("config: market source driver is empty") + return nil, merrors.InvalidArgument("config: market source driver is empty") } sourceSet[src.Driver] = struct{}{} } if len(cfg.Market.Pairs) == 0 { - return nil, fmerrors.New("config: no pairs configured") + return nil, merrors.InvalidArgument("config: no pairs configured") } normalizedPairs := make(map[string][]PairConfig, len(cfg.Market.Pairs)) @@ -61,10 +61,10 @@ func Load(path string) (*Config, error) { for rawSource, pairList := range cfg.Market.Pairs { driver := mmodel.Driver(rawSource) if driver.IsEmpty() { - return nil, fmerrors.New("config: pair source is empty") + return nil, merrors.InvalidArgument("config: pair source is empty") } if _, ok := sourceSet[driver]; !ok { - return nil, fmerrors.New("config: pair references unknown source: " + driver.String()) + return nil, merrors.InvalidArgument("config: pair references unknown source: "+driver.String(), "pairs."+driver.String()) } processed := make([]PairConfig, len(pairList)) @@ -74,7 +74,7 @@ func Load(path string) (*Config, error) { pair.Quote = strings.ToUpper(strings.TrimSpace(pair.Quote)) pair.Symbol = strings.TrimSpace(pair.Symbol) if pair.Base == "" || pair.Quote == "" || pair.Symbol == "" { - return nil, fmerrors.New("config: pair entries must define base, quote, and symbol") + return nil, merrors.InvalidArgument("config: pair entries must define base, quote, and symbol", "pairs."+driver.String()) } if strings.TrimSpace(pair.Provider) == "" { pair.Provider = strings.ToLower(driver.String()) @@ -93,7 +93,7 @@ func Load(path string) (*Config, error) { cfg.pairsBySource = pairsBySource cfg.pairs = flattened if cfg.Database == nil { - return nil, fmerrors.New("config: database configuration is required") + return nil, merrors.InvalidArgument("config: database configuration is required") } if cfg.Metrics != nil && cfg.Metrics.Enabled { diff --git a/api/fx/ingestor/internal/fmerrors/market.go b/api/fx/ingestor/internal/fmerrors/market.go deleted file mode 100644 index a21ba63..0000000 --- a/api/fx/ingestor/internal/fmerrors/market.go +++ /dev/null @@ -1,35 +0,0 @@ -package fmerrors - -type Error struct { - message string - cause error -} - -func (e *Error) Error() string { - if e == nil { - return "" - } - if e.cause == nil { - return e.message - } - return e.message + ": " + e.cause.Error() -} - -func (e *Error) Unwrap() error { - if e == nil { - return nil - } - return e.cause -} - -func New(message string) error { - return &Error{message: message} -} - -func Wrap(message string, cause error) error { - return &Error{message: message, cause: cause} -} - -func NewDecimal(value string) error { - return &Error{message: "invalid decimal \"" + value + "\""} -} diff --git a/api/fx/ingestor/internal/ingestor/service.go b/api/fx/ingestor/internal/ingestor/service.go index a3f8c54..75bc752 100644 --- a/api/fx/ingestor/internal/ingestor/service.go +++ b/api/fx/ingestor/internal/ingestor/service.go @@ -6,11 +6,11 @@ import ( "time" "github.com/tech/sendico/fx/ingestor/internal/config" - "github.com/tech/sendico/fx/ingestor/internal/fmerrors" "github.com/tech/sendico/fx/ingestor/internal/market" mmodel "github.com/tech/sendico/fx/ingestor/internal/model" "github.com/tech/sendico/fx/storage" "github.com/tech/sendico/fx/storage/model" + "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/mlogger" "go.uber.org/zap" ) @@ -26,18 +26,18 @@ type Service struct { func New(logger mlogger.Logger, cfg *config.Config, repo storage.Repository) (*Service, error) { if logger == nil { - return nil, fmerrors.New("ingestor: nil logger") + return nil, merrors.InvalidArgument("ingestor: nil logger") } if cfg == nil { - return nil, fmerrors.New("ingestor: nil config") + return nil, merrors.InvalidArgument("ingestor: nil config") } if repo == nil { - return nil, fmerrors.New("ingestor: nil repository") + return nil, merrors.InvalidArgument("ingestor: nil repository") } connectors, err := market.BuildConnectors(logger, cfg.Market.Sources) if err != nil { - return nil, fmerrors.Wrap("build connectors", err) + return nil, merrors.InternalWrap(err, "build connectors") } return &Service{ @@ -110,21 +110,21 @@ func (s *Service) pollOnce(ctx context.Context) error { func (s *Service) upsertPair(ctx context.Context, pair config.Pair) error { connector, ok := s.connectors[pair.Source] if !ok { - return fmerrors.Wrap("connector not configured for source "+pair.Source.String(), nil) + return merrors.InvalidArgument("connector not configured for source "+pair.Source.String(), "source") } ticker, err := connector.FetchTicker(ctx, pair.Symbol) if err != nil { - return fmerrors.Wrap("fetch ticker", err) + return merrors.InternalWrap(err, "fetch ticker") } bid, err := parseDecimal(ticker.BidPrice) if err != nil { - return fmerrors.Wrap("parse bid price", err) + return merrors.InvalidArgumentWrap(err, "parse bid price", "bid") } ask, err := parseDecimal(ticker.AskPrice) if err != nil { - return fmerrors.Wrap("parse ask price", err) + return merrors.InvalidArgumentWrap(err, "parse ask price", "ask") } if pair.Invert { @@ -166,7 +166,7 @@ func (s *Service) upsertPair(ctx context.Context, pair config.Pair) error { } if err := s.rates.UpsertSnapshot(ctx, snapshot); err != nil { - return fmerrors.Wrap("upsert snapshot", err) + return merrors.InternalWrap(err, "upsert snapshot") } s.logger.Debug("Snapshot ingested", @@ -183,7 +183,7 @@ func (s *Service) upsertPair(ctx context.Context, pair config.Pair) error { func parseDecimal(value string) (*big.Rat, error) { r := new(big.Rat) if _, ok := r.SetString(value); !ok { - return nil, fmerrors.NewDecimal(value) + return nil, merrors.InvalidArgument("invalid decimal \""+value+"\"", "value") } return r, nil } diff --git a/api/fx/ingestor/internal/ingestor/service_test.go b/api/fx/ingestor/internal/ingestor/service_test.go index 63fc931..16f9b2e 100644 --- a/api/fx/ingestor/internal/ingestor/service_test.go +++ b/api/fx/ingestor/internal/ingestor/service_test.go @@ -7,10 +7,10 @@ import ( "github.com/google/go-cmp/cmp" "github.com/tech/sendico/fx/ingestor/internal/config" - "github.com/tech/sendico/fx/ingestor/internal/fmerrors" mmarket "github.com/tech/sendico/fx/ingestor/internal/model" "github.com/tech/sendico/fx/storage" "github.com/tech/sendico/fx/storage/model" + "github.com/tech/sendico/pkg/merrors" "go.uber.org/zap" ) @@ -131,7 +131,7 @@ func TestServiceUpsertPairInvertsPrices(t *testing.T) { } func TestServicePollOnceReturnsFirstError(t *testing.T) { - errFetch := fmerrors.New("fetch failed") + errFetch := merrors.Internal("fetch failed") connectorSuccess := &connectorStub{ id: mmarket.DriverBinance, ticker: &mmarket.Ticker{ diff --git a/api/fx/ingestor/internal/market/binance/connector.go b/api/fx/ingestor/internal/market/binance/connector.go index f46e131..809d89d 100644 --- a/api/fx/ingestor/internal/market/binance/connector.go +++ b/api/fx/ingestor/internal/market/binance/connector.go @@ -10,9 +10,9 @@ import ( "strings" "time" - "github.com/tech/sendico/fx/ingestor/internal/fmerrors" "github.com/tech/sendico/fx/ingestor/internal/market/common" mmodel "github.com/tech/sendico/fx/ingestor/internal/model" + "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/mlogger" "github.com/tech/sendico/pkg/model" "go.uber.org/zap" @@ -60,7 +60,7 @@ func NewConnector(logger mlogger.Logger, settings model.SettingsT) (mmodel.Conne parsed, err := url.Parse(baseURL) if err != nil { - return nil, fmerrors.Wrap("binance: invalid base url", err) + return nil, merrors.InvalidArgumentWrap(err, "binance: invalid base url", "base_url") } transport := &http.Transport{ @@ -89,12 +89,12 @@ func (c *binanceConnector) ID() mmodel.Driver { func (c *binanceConnector) FetchTicker(ctx context.Context, symbol string) (*mmodel.Ticker, error) { if strings.TrimSpace(symbol) == "" { - return nil, fmerrors.New("binance: symbol is empty") + return nil, merrors.InvalidArgument("binance: symbol is empty", "symbol") } endpoint, err := url.Parse(c.base) if err != nil { - return nil, fmerrors.Wrap("binance: parse base url", err) + return nil, merrors.InternalWrap(err, "binance: parse base url") } endpoint.Path = "/api/v3/ticker/bookTicker" query := endpoint.Query() @@ -103,19 +103,19 @@ func (c *binanceConnector) FetchTicker(ctx context.Context, symbol string) (*mmo req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil) if err != nil { - return nil, fmerrors.Wrap("binance: build request", err) + return nil, merrors.InternalWrap(err, "binance: build request") } resp, err := c.client.Do(req) if err != nil { c.logger.Warn("Binance request failed", zap.String("symbol", symbol), zap.Error(err)) - return nil, fmerrors.Wrap("binance: request failed", err) + return nil, merrors.InternalWrap(err, "binance: request failed") } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { c.logger.Warn("Binance returned non-OK status", zap.String("symbol", symbol), zap.Int("status", resp.StatusCode)) - return nil, fmerrors.New("binance: unexpected status " + strconv.Itoa(resp.StatusCode)) + return nil, merrors.Internal("binance: unexpected status " + strconv.Itoa(resp.StatusCode)) } var payload struct { @@ -126,7 +126,7 @@ func (c *binanceConnector) FetchTicker(ctx context.Context, symbol string) (*mmo if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil { c.logger.Warn("Binance decode failed", zap.String("symbol", symbol), zap.Error(err)) - return nil, fmerrors.Wrap("binance: decode response", err) + return nil, merrors.InternalWrap(err, "binance: decode response") } return &mmodel.Ticker{ diff --git a/api/fx/ingestor/internal/market/cbr/connector.go b/api/fx/ingestor/internal/market/cbr/connector.go new file mode 100644 index 0000000..ed4f6fb --- /dev/null +++ b/api/fx/ingestor/internal/market/cbr/connector.go @@ -0,0 +1,537 @@ +package cbr + +import ( + "context" + "encoding/xml" + "math/big" + "net" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "github.com/tech/sendico/fx/ingestor/internal/market/common" + mmodel "github.com/tech/sendico/fx/ingestor/internal/model" + "github.com/tech/sendico/pkg/merrors" + "github.com/tech/sendico/pkg/mlogger" + "github.com/tech/sendico/pkg/model" + "go.uber.org/zap" + "golang.org/x/net/html/charset" +) + +type cbrConnector struct { + id mmodel.Driver + provider string + client *http.Client + base string + dailyPath string + directoryPath string + dynamicPath string + logger mlogger.Logger + + byISO map[string]valuteInfo + byID map[string]valuteInfo +} + +const defaultCBRBaseURL = "https://www.cbr.ru" +const ( + defaultDirectoryPath = "/scripts/XML_valFull.asp" + defaultDailyPath = "/scripts/XML_daily.asp" + defaultDynamicPath = "/scripts/XML_dynamic.asp" +) + +const ( + defaultDialTimeoutSeconds = 5 * time.Second + defaultDialKeepAliveSeconds = 30 * time.Second + defaultTLSHandshakeTimeoutSeconds = 5 * time.Second + defaultResponseHeaderTimeoutSeconds = 10 * time.Second + defaultRequestTimeoutSeconds = 10 * time.Second +) + +func NewConnector(logger mlogger.Logger, settings model.SettingsT) (mmodel.Connector, error) { + baseURL := defaultCBRBaseURL + provider := strings.ToLower(mmodel.DriverCBR.String()) + dialTimeout := defaultDialTimeoutSeconds + dialKeepAlive := defaultDialKeepAliveSeconds + tlsHandshakeTimeout := defaultTLSHandshakeTimeoutSeconds + responseHeaderTimeout := defaultResponseHeaderTimeoutSeconds + requestTimeout := defaultRequestTimeoutSeconds + directoryPath := defaultDirectoryPath + dailyPath := defaultDailyPath + dynamicPath := defaultDynamicPath + + if settings != nil { + if value, ok := settings["base_url"].(string); ok && strings.TrimSpace(value) != "" { + baseURL = strings.TrimSpace(value) + } + if value, ok := settings["provider"].(string); ok && strings.TrimSpace(value) != "" { + provider = strings.TrimSpace(value) + } + if value, ok := settings["directory_path"].(string); ok && strings.TrimSpace(value) != "" { + directoryPath = strings.TrimSpace(value) + } + if value, ok := settings["daily_path"].(string); ok && strings.TrimSpace(value) != "" { + dailyPath = strings.TrimSpace(value) + } + if value, ok := settings["dynamic_path"].(string); ok && strings.TrimSpace(value) != "" { + dynamicPath = strings.TrimSpace(value) + } + dialTimeout = common.DurationSetting(settings, "dial_timeout_seconds", dialTimeout) + dialKeepAlive = common.DurationSetting(settings, "dial_keep_alive_seconds", dialKeepAlive) + tlsHandshakeTimeout = common.DurationSetting(settings, "tls_handshake_timeout_seconds", tlsHandshakeTimeout) + responseHeaderTimeout = common.DurationSetting(settings, "response_header_timeout_seconds", responseHeaderTimeout) + requestTimeout = common.DurationSetting(settings, "request_timeout_seconds", requestTimeout) + } + + parsed, err := url.Parse(baseURL) + if err != nil { + return nil, merrors.InvalidArgumentWrap(err, "cbr: invalid base url", "base_url") + } + + var transport http.RoundTripper = &http.Transport{ + DialContext: (&net.Dialer{Timeout: dialTimeout, KeepAlive: dialKeepAlive}).DialContext, + TLSHandshakeTimeout: tlsHandshakeTimeout, + ResponseHeaderTimeout: responseHeaderTimeout, + } + + if customTransport, ok := settings["http_round_tripper"].(http.RoundTripper); ok && customTransport != nil { + transport = customTransport + } + + connector := &cbrConnector{ + id: mmodel.DriverCBR, + provider: provider, + client: &http.Client{ + Timeout: requestTimeout, + Transport: transport, + }, + base: strings.TrimRight(parsed.String(), "/"), + dailyPath: dailyPath, + directoryPath: directoryPath, + dynamicPath: dynamicPath, + logger: logger.Named("cbr"), + } + + if err := connector.refreshDirectory(); err != nil { + return nil, err + } + + return connector, nil +} + +func (c *cbrConnector) ID() mmodel.Driver { + return c.id +} + +func (c *cbrConnector) FetchTicker(ctx context.Context, symbol string) (*mmodel.Ticker, error) { + isoCode, asOfDate, err := parseSymbol(symbol) + if err != nil { + return nil, err + } + + valute, ok := c.byISO[isoCode] + if !ok { + return nil, merrors.InvalidArgument("cbr: unknown currency "+isoCode, "symbol") + } + + var price string + if asOfDate != nil { + price, err = c.fetchHistoricalRate(ctx, valute, *asOfDate) + } else { + price, err = c.fetchDailyRate(ctx, valute) + } + if err != nil { + return nil, err + } + + now := time.Now().UnixMilli() + return &mmodel.Ticker{ + Symbol: formatSymbol(isoCode, asOfDate), + BidPrice: price, + AskPrice: price, + Provider: c.provider, + Timestamp: now, + }, nil +} + +func (c *cbrConnector) refreshDirectory() error { + endpoint, err := c.buildURL(c.directoryPath, nil) + if err != nil { + return err + } + + req, err := http.NewRequest(http.MethodGet, endpoint, nil) + if err != nil { + return merrors.InternalWrap(err, "cbr: build directory request") + } + + resp, err := c.client.Do(req) + if err != nil { + c.logger.Warn("CBR directory request failed", zap.Error(err)) + return merrors.InternalWrap(err, "cbr: directory request failed") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + c.logger.Warn("CBR directory returned non-OK status", zap.Int("status", resp.StatusCode)) + return merrors.Internal("cbr: unexpected status " + strconv.Itoa(resp.StatusCode)) + } + + decoder := xml.NewDecoder(resp.Body) + decoder.CharsetReader = charset.NewReaderLabel + + var directory valuteDirectory + if err := decoder.Decode(&directory); err != nil { + c.logger.Warn("CBR directory decode failed", zap.Error(err)) + return merrors.InternalWrap(err, "cbr: decode directory") + } + + mapping, err := buildValuteMapping(directory.Items) + if err != nil { + return err + } + + c.byISO = mapping.byISO + c.byID = mapping.byID + return nil +} + +func (c *cbrConnector) fetchDailyRate(ctx context.Context, valute valuteInfo) (string, error) { + endpoint, err := c.buildURL(c.dailyPath, nil) + if err != nil { + return "", err + } + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) + if err != nil { + return "", merrors.InternalWrap(err, "cbr: build daily request") + } + + resp, err := c.client.Do(req) + if err != nil { + c.logger.Warn("CBR daily request failed", zap.String("currency", valute.ISOCharCode), zap.Error(err)) + return "", merrors.InternalWrap(err, "cbr: daily request failed") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + c.logger.Warn("CBR daily returned non-OK status", zap.Int("status", resp.StatusCode)) + return "", merrors.Internal("cbr: unexpected status " + strconv.Itoa(resp.StatusCode)) + } + + decoder := xml.NewDecoder(resp.Body) + decoder.CharsetReader = charset.NewReaderLabel + + var payload dailyRates + if err := decoder.Decode(&payload); err != nil { + c.logger.Warn("CBR daily decode failed", zap.Error(err)) + return "", merrors.InternalWrap(err, "cbr: decode daily response") + } + + entry := payload.find(valute.ID) + if entry == nil { + return "", merrors.NoData("cbr: currency not found in daily rates: " + valute.ISOCharCode) + } + + if err := validateDailyEntry(valute, entry); err != nil { + return "", err + } + + return computePrice(entry.Value, entry.Nominal) +} + +func (c *cbrConnector) fetchHistoricalRate(ctx context.Context, valute valuteInfo, date time.Time) (string, error) { + query := map[string]string{ + "date_req1": date.Format("02/01/2006"), + "date_req2": date.Format("02/01/2006"), + "VAL_NM_RQ": valute.ID, + } + endpoint, err := c.buildURL(c.dynamicPath, query) + if err != nil { + return "", err + } + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) + if err != nil { + return "", merrors.InternalWrap(err, "cbr: build historical request") + } + + resp, err := c.client.Do(req) + if err != nil { + c.logger.Warn("CBR historical request failed", zap.String("currency", valute.ISOCharCode), zap.Error(err)) + return "", merrors.InternalWrap(err, "cbr: historical request failed") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + c.logger.Warn("CBR historical returned non-OK status", zap.Int("status", resp.StatusCode)) + return "", merrors.Internal("cbr: unexpected status " + strconv.Itoa(resp.StatusCode)) + } + + decoder := xml.NewDecoder(resp.Body) + decoder.CharsetReader = charset.NewReaderLabel + + var payload dynamicRates + if err := decoder.Decode(&payload); err != nil { + c.logger.Warn("CBR historical decode failed", zap.Error(err)) + return "", merrors.InternalWrap(err, "cbr: decode historical response") + } + + record := payload.find(valute.ID, date) + if record == nil { + return "", merrors.NoData("cbr: historical rate not found for " + valute.ISOCharCode) + } + + if record.Nominal != "" { + nominal, err := parseNominal(record.Nominal) + if err != nil { + return "", merrors.InvalidDataType(err.Error()) + } + if nominal != valute.Nominal { + return "", merrors.Internal("cbr: historical nominal mismatch for " + valute.ISOCharCode) + } + } + + return computePrice(record.Value, strconv.FormatInt(valute.Nominal, 10)) +} + +func (c *cbrConnector) buildURL(path string, query map[string]string) (string, error) { + base, err := url.Parse(c.base) + if err != nil { + return "", merrors.InternalWrap(err, "cbr: parse base url") + } + base.Path = strings.TrimRight(base.Path, "/") + path + q := base.Query() + for key, value := range query { + q.Set(key, value) + } + base.RawQuery = q.Encode() + return base.String(), nil +} + +type valuteDirectory struct { + Items []valuteItem `xml:"Item"` +} + +type valuteItem struct { + ID string `xml:"ID,attr"` + ISOChar string `xml:"ISO_Char_Code"` + ISONum string `xml:"ISO_Num_Code"` + Name string `xml:"Name"` + EngName string `xml:"EngName"` + NominalStr string `xml:"Nominal"` +} + +type valuteInfo struct { + ID string + ISOCharCode string + ISONumCode string + Name string + EngName string + Nominal int64 +} + +type valuteMapping struct { + byISO map[string]valuteInfo + byID map[string]valuteInfo +} + +func buildValuteMapping(items []valuteItem) (*valuteMapping, error) { + byISO := make(map[string]valuteInfo, len(items)) + byID := make(map[string]valuteInfo, len(items)) + byNum := make(map[string]string, len(items)) + + for _, item := range items { + id := strings.TrimSpace(item.ID) + isoChar := strings.ToUpper(strings.TrimSpace(item.ISOChar)) + isoNum := strings.TrimSpace(item.ISONum) + name := strings.TrimSpace(item.Name) + engName := strings.TrimSpace(item.EngName) + nominal, err := parseNominal(item.NominalStr) + if err != nil { + return nil, merrors.InvalidDataType("cbr: parse directory nominal: " + err.Error()) + } + if id == "" || isoChar == "" { + return nil, merrors.InvalidDataType("cbr: directory contains entry with empty id or iso code") + } + + info := valuteInfo{ + ID: id, + ISOCharCode: isoChar, + ISONumCode: isoNum, + Name: name, + EngName: engName, + Nominal: nominal, + } + + if existing, ok := byISO[isoChar]; ok && existing.ID != id { + return nil, merrors.InvalidDataType("cbr: duplicate ISO code " + isoChar) + } + if existing, ok := byID[id]; ok && existing.ISOCharCode != isoChar { + return nil, merrors.InvalidDataType("cbr: duplicate valute id " + id) + } + if isoNum != "" { + if existingID, ok := byNum[isoNum]; ok && existingID != id { + return nil, merrors.InvalidDataType("cbr: duplicate ISO numeric code " + isoNum) + } + byNum[isoNum] = id + } + + byISO[isoChar] = info + byID[id] = info + } + + if len(byISO) == 0 { + return nil, merrors.InvalidDataType("cbr: empty directory received") + } + + return &valuteMapping{ + byISO: byISO, + byID: byID, + }, nil +} + +type dailyRates struct { + Valutes []dailyValute `xml:"Valute"` +} + +type dailyValute struct { + ID string `xml:"ID,attr"` + NumCode string `xml:"NumCode"` + CharCode string `xml:"CharCode"` + Nominal string `xml:"Nominal"` + Name string `xml:"Name"` + Value string `xml:"Value"` +} + +func (d *dailyRates) find(id string) *dailyValute { + if d == nil { + return nil + } + for idx := range d.Valutes { + if strings.EqualFold(strings.TrimSpace(d.Valutes[idx].ID), id) { + return &d.Valutes[idx] + } + } + return nil +} + +type dynamicRates struct { + Records []dynamicRecord `xml:"Record"` +} + +type dynamicRecord struct { + ID string `xml:"Id,attr"` + DateRaw string `xml:"Date,attr"` + Nominal string `xml:"Nominal"` + Value string `xml:"Value"` +} + +func (d *dynamicRates) find(id string, date time.Time) *dynamicRecord { + if d == nil { + return nil + } + target := date.Format("02.01.2006") + for idx := range d.Records { + rec := &d.Records[idx] + if !strings.EqualFold(strings.TrimSpace(rec.ID), id) { + continue + } + if strings.TrimSpace(rec.DateRaw) == target { + return rec + } + } + return nil +} + +func validateDailyEntry(expected valuteInfo, entry *dailyValute) error { + if entry == nil { + return merrors.NoData("cbr: missing daily entry") + } + if !strings.EqualFold(strings.TrimSpace(entry.CharCode), expected.ISOCharCode) { + return merrors.Internal("cbr: char code mismatch for " + expected.ISOCharCode) + } + if expected.ISONumCode != "" && strings.TrimSpace(entry.NumCode) != expected.ISONumCode { + return merrors.Internal("cbr: iso numeric mismatch for " + expected.ISOCharCode) + } + if expected.Name != "" && strings.TrimSpace(entry.Name) != expected.Name { + return merrors.Internal("cbr: currency name mismatch for " + expected.ISOCharCode) + } + + nominal, err := parseNominal(entry.Nominal) + if err != nil { + return merrors.InvalidDataType("cbr: parse daily nominal: " + err.Error()) + } + if nominal != expected.Nominal { + return merrors.Internal("cbr: nominal mismatch for " + expected.ISOCharCode) + } + return nil +} + +func parseSymbol(symbol string) (string, *time.Time, error) { + trimmed := strings.TrimSpace(symbol) + if trimmed == "" { + return "", nil, merrors.InvalidArgument("cbr: symbol is empty", "symbol") + } + + parts := strings.Split(trimmed, "@") + if len(parts) > 2 { + return "", nil, merrors.InvalidArgument("cbr: invalid symbol format", "symbol") + } + + iso := strings.ToUpper(strings.TrimSpace(parts[0])) + if len(iso) != 3 { + return "", nil, merrors.InvalidArgument("cbr: symbol must be ISO currency code", "symbol") + } + + if len(parts) == 1 { + return iso, nil, nil + } + + datePart := strings.TrimSpace(parts[1]) + if datePart == "" { + return "", nil, merrors.InvalidArgument("cbr: date component is empty", "symbol") + } + + parsed, err := time.Parse("2006-01-02", datePart) + if err != nil { + return "", nil, merrors.InvalidArgumentWrap(err, "cbr: invalid date component", "symbol") + } + + return iso, &parsed, nil +} + +func parseNominal(value string) (int64, error) { + nominal, err := strconv.ParseInt(strings.TrimSpace(value), 10, 64) + if err != nil || nominal <= 0 { + return 0, merrors.InvalidDataType("cbr: invalid nominal \"" + value + "\"") + } + return nominal, nil +} + +func computePrice(value string, nominalStr string) (string, error) { + raw := strings.ReplaceAll(strings.TrimSpace(value), " ", "") + raw = strings.ReplaceAll(raw, ",", ".") + + r := new(big.Rat) + if _, ok := r.SetString(raw); !ok { + return "", merrors.InvalidDataType("invalid decimal \"" + value + "\"") + } + + nominal, err := parseNominal(nominalStr) + if err != nil { + return "", err + } + + den := big.NewRat(nominal, 1) + price := new(big.Rat).Quo(r, den) + return price.FloatString(8), nil +} + +func formatSymbol(iso string, asOf *time.Time) string { + if asOf == nil { + return iso + } + return iso + "@" + asOf.Format("2006-01-02") +} diff --git a/api/fx/ingestor/internal/market/cbr/connector_test.go b/api/fx/ingestor/internal/market/cbr/connector_test.go new file mode 100644 index 0000000..d57cc9b --- /dev/null +++ b/api/fx/ingestor/internal/market/cbr/connector_test.go @@ -0,0 +1,226 @@ +package cbr + +import ( + "context" + "errors" + "fmt" + "io" + "net/http" + "strings" + "testing" + + "github.com/tech/sendico/pkg/merrors" + "go.uber.org/zap" +) + +func TestFetchTickerDaily(t *testing.T) { + transport := &stubRoundTripper{ + responses: map[string]stubResponse{ + "/scripts/XML_valFull.asp": {body: valuteDirectoryXML}, + "/scripts/XML_daily.asp": {body: dailyRatesXML}, + }, + } + + conn, err := NewConnector(zap.NewNop(), map[string]any{ + "base_url": "http://cbr.test", + "http_round_tripper": transport, + "request_timeout_seconds": 1, + }) + if err != nil { + t.Fatalf("NewConnector returned error: %v", err) + } + + ticker, err := conn.FetchTicker(context.Background(), "USD") + if err != nil { + t.Fatalf("FetchTicker returned error: %v", err) + } + + if ticker.Provider != "cbr" { + t.Fatalf("unexpected provider: %s", ticker.Provider) + } + if ticker.BidPrice != "95.12340000" || ticker.AskPrice != "95.12340000" { + t.Fatalf("unexpected bid/ask: %s / %s", ticker.BidPrice, ticker.AskPrice) + } + if ticker.Symbol != "USD" { + t.Fatalf("unexpected symbol: %s", ticker.Symbol) + } +} + +func TestFetchTickerValidatesDailyEntry(t *testing.T) { + transport := &stubRoundTripper{ + responses: map[string]stubResponse{ + "/scripts/XML_valFull.asp": {body: valuteDirectoryXML}, + "/scripts/XML_daily.asp": {body: strings.ReplaceAll(dailyRatesXML, "USD", "XXX")}, + }, + } + + conn, err := NewConnector(zap.NewNop(), map[string]any{ + "base_url": "http://cbr.test", + "http_round_tripper": transport, + }) + if err != nil { + t.Fatalf("NewConnector returned error: %v", err) + } + + if _, err := conn.FetchTicker(context.Background(), "USD"); err == nil { + t.Fatalf("FetchTicker expected to fail due to mismatch") + } +} + +func TestFetchTickerHistorical(t *testing.T) { + transport := &stubRoundTripper{ + responses: map[string]stubResponse{ + "/scripts/XML_valFull.asp": {body: valuteDirectoryXML}, + "/scripts/XML_dynamic.asp": { + body: dynamicRatesXML, + check: func(r *http.Request) error { + if got := r.URL.Query().Get("VAL_NM_RQ"); got != "R01235" { + return fmt.Errorf("unexpected valute id: %s", got) + } + if got := r.URL.Query().Get("date_req1"); got != "05/01/2023" { + return fmt.Errorf("unexpected date_req1: %s", got) + } + if got := r.URL.Query().Get("date_req2"); got != "05/01/2023" { + return fmt.Errorf("unexpected date_req2: %s", got) + } + return nil + }, + }, + }, + } + + conn, err := NewConnector(zap.NewNop(), map[string]any{ + "base_url": "http://cbr.test", + "http_round_tripper": transport, + }) + if err != nil { + t.Fatalf("NewConnector returned error: %v", err) + } + + ticker, err := conn.FetchTicker(context.Background(), "USD@2023-01-05") + if err != nil { + t.Fatalf("FetchTicker returned error: %v", err) + } + + if ticker.BidPrice != "70.10000000" || ticker.AskPrice != "70.10000000" { + t.Fatalf("unexpected bid/ask: %s / %s", ticker.BidPrice, ticker.AskPrice) + } + if ticker.Symbol != "USD@2023-01-05" { + t.Fatalf("unexpected symbol: %s", ticker.Symbol) + } +} + +func TestFetchTickerUnknownCurrency(t *testing.T) { + transport := &stubRoundTripper{ + responses: map[string]stubResponse{ + "/scripts/XML_valFull.asp": {body: valuteDirectoryXML}, + "/scripts/XML_daily.asp": {body: dailyRatesXML}, + }, + } + + conn, err := NewConnector(zap.NewNop(), map[string]any{ + "base_url": "http://cbr.test", + "http_round_tripper": transport, + }) + if err != nil { + t.Fatalf("NewConnector returned error: %v", err) + } + + _, err = conn.FetchTicker(context.Background(), "ZZZ") + if err == nil { + t.Fatalf("FetchTicker expected to fail for unknown currency") + } + if !errors.Is(err, merrors.ErrInvalidArg) { + t.Fatalf("expected invalid argument error, got %v", err) + } +} + +func TestFetchTickerRespectsCustomPaths(t *testing.T) { + transport := &stubRoundTripper{ + responses: map[string]stubResponse{ + "/dir.xml": {body: valuteDirectoryXML}, + "/rates.xml": {body: dailyRatesXML}, + }, + } + + conn, err := NewConnector(zap.NewNop(), map[string]any{ + "base_url": "http://cbr.test", + "directory_path": "/dir.xml", + "daily_path": "/rates.xml", + "http_round_tripper": transport, + }) + if err != nil { + t.Fatalf("NewConnector returned error: %v", err) + } + + if _, err := conn.FetchTicker(context.Background(), "USD"); err != nil { + t.Fatalf("FetchTicker returned error with custom paths: %v", err) + } +} + +const valuteDirectoryXML = ` + + + 840 + USD + 1 + US Dollar + US Dollar + +` + +const dailyRatesXML = ` + + + 840 + USD + 1 + US Dollar + 95,1234 + +` + +const dynamicRatesXML = ` + + + 1 + 70,1 + +` + +type stubResponse struct { + status int + body string + check func(*http.Request) error +} + +type stubRoundTripper struct { + responses map[string]stubResponse +} + +func (s *stubRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + if s.responses == nil { + return nil, fmt.Errorf("no responses configured") + } + res, ok := s.responses[req.URL.Path] + if !ok { + return nil, fmt.Errorf("unexpected request path: %s", req.URL.Path) + } + if res.check != nil { + if err := res.check(req); err != nil { + return nil, err + } + } + + status := res.status + if status == 0 { + status = http.StatusOK + } + + return &http.Response{ + StatusCode: status, + Body: io.NopCloser(strings.NewReader(res.body)), + Header: http.Header{"Content-Type": []string{"text/xml"}}, + Request: req, + }, nil +} diff --git a/api/fx/ingestor/internal/market/coingecko/connector.go b/api/fx/ingestor/internal/market/coingecko/connector.go index 9b878f5..36ecb46 100644 --- a/api/fx/ingestor/internal/market/coingecko/connector.go +++ b/api/fx/ingestor/internal/market/coingecko/connector.go @@ -10,9 +10,9 @@ import ( "strings" "time" - "github.com/tech/sendico/fx/ingestor/internal/fmerrors" "github.com/tech/sendico/fx/ingestor/internal/market/common" mmodel "github.com/tech/sendico/fx/ingestor/internal/model" + "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/mlogger" "github.com/tech/sendico/pkg/model" "go.uber.org/zap" @@ -61,7 +61,7 @@ func NewConnector(logger mlogger.Logger, settings model.SettingsT) (mmodel.Conne parsed, err := url.Parse(baseURL) if err != nil { - return nil, fmerrors.Wrap("coingecko: invalid base url", err) + return nil, merrors.InvalidArgumentWrap(err, "coingecko: invalid base url", "base_url") } transport := &http.Transport{ @@ -96,7 +96,7 @@ func (c *coingeckoConnector) FetchTicker(ctx context.Context, symbol string) (*m endpoint, err := url.Parse(c.base) if err != nil { - return nil, fmerrors.Wrap("coingecko: parse base url", err) + return nil, merrors.InternalWrap(err, "coingecko: parse base url") } endpoint.Path = strings.TrimRight(endpoint.Path, "/") + "/simple/price" query := endpoint.Query() @@ -107,19 +107,19 @@ func (c *coingeckoConnector) FetchTicker(ctx context.Context, symbol string) (*m req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil) if err != nil { - return nil, fmerrors.Wrap("coingecko: build request", err) + return nil, merrors.InternalWrap(err, "coingecko: build request") } resp, err := c.client.Do(req) if err != nil { c.logger.Warn("CoinGecko request failed", zap.String("symbol", symbol), zap.Error(err)) - return nil, fmerrors.Wrap("coingecko: request failed", err) + return nil, merrors.InternalWrap(err, "coingecko: request failed") } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { c.logger.Warn("CoinGecko returned non-OK status", zap.String("symbol", symbol), zap.Int("status", resp.StatusCode)) - return nil, fmerrors.New("coingecko: unexpected status " + strconv.Itoa(resp.StatusCode)) + return nil, merrors.Internal("coingecko: unexpected status " + strconv.Itoa(resp.StatusCode)) } decoder := json.NewDecoder(resp.Body) @@ -128,21 +128,21 @@ func (c *coingeckoConnector) FetchTicker(ctx context.Context, symbol string) (*m var payload map[string]map[string]interface{} if err := decoder.Decode(&payload); err != nil { c.logger.Warn("CoinGecko decode failed", zap.String("symbol", symbol), zap.Error(err)) - return nil, fmerrors.Wrap("coingecko: decode response", err) + return nil, merrors.InternalWrap(err, "coingecko: decode response") } coinData, ok := payload[coinID] if !ok { - return nil, fmerrors.New("coingecko: coin id not found in response") + return nil, merrors.Internal("coingecko: coin id not found in response") } priceValue, ok := coinData[vsCurrency] if !ok { - return nil, fmerrors.New("coingecko: vs currency not found in response") + return nil, merrors.Internal("coingecko: vs currency not found in response") } price, ok := toFloat(priceValue) if !ok || price <= 0 { - return nil, fmerrors.New("coingecko: invalid price value in response") + return nil, merrors.Internal("coingecko: invalid price value in response") } priceStr := strconv.FormatFloat(price, 'f', -1, 64) @@ -171,7 +171,7 @@ func (c *coingeckoConnector) FetchTicker(ctx context.Context, symbol string) (*m func parseSymbol(symbol string) (string, string, error) { trimmed := strings.TrimSpace(symbol) if trimmed == "" { - return "", "", fmerrors.New("coingecko: symbol is empty") + return "", "", merrors.InvalidArgument("coingecko: symbol is empty", "symbol") } parts := strings.FieldsFunc(strings.ToLower(trimmed), func(r rune) bool { @@ -183,13 +183,13 @@ func parseSymbol(symbol string) (string, string, error) { }) if len(parts) != 2 { - return "", "", fmerrors.New("coingecko: symbol must be /") + return "", "", merrors.InvalidArgument("coingecko: symbol must be /", "symbol") } coinID := strings.TrimSpace(parts[0]) vsCurrency := strings.TrimSpace(parts[1]) if coinID == "" || vsCurrency == "" { - return "", "", fmerrors.New("coingecko: symbol contains empty segments") + return "", "", merrors.InvalidArgument("coingecko: symbol contains empty segments", "symbol") } return coinID, vsCurrency, nil diff --git a/api/fx/ingestor/internal/market/factory.go b/api/fx/ingestor/internal/market/factory.go index e2d0a5f..86f6034 100644 --- a/api/fx/ingestor/internal/market/factory.go +++ b/api/fx/ingestor/internal/market/factory.go @@ -5,10 +5,11 @@ import ( "strings" "time" - "github.com/tech/sendico/fx/ingestor/internal/fmerrors" "github.com/tech/sendico/fx/ingestor/internal/market/binance" + "github.com/tech/sendico/fx/ingestor/internal/market/cbr" "github.com/tech/sendico/fx/ingestor/internal/market/coingecko" mmodel "github.com/tech/sendico/fx/ingestor/internal/model" + "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/mlogger" "github.com/tech/sendico/pkg/model" ) @@ -21,7 +22,7 @@ func BuildConnectors(logger mlogger.Logger, configs []model.DriverConfig[mmodel. for _, cfg := range configs { driver := mmodel.NormalizeDriver(cfg.Driver) if driver.IsEmpty() { - return nil, fmerrors.New("market: connector driver is empty") + return nil, merrors.InvalidArgument("market: connector driver is empty", "driver") } var ( @@ -34,12 +35,14 @@ func BuildConnectors(logger mlogger.Logger, configs []model.DriverConfig[mmodel. conn, err = binance.NewConnector(logger, cfg.Settings) case mmodel.DriverCoinGecko: conn, err = coingecko.NewConnector(logger, cfg.Settings) + case mmodel.DriverCBR: + conn, err = cbr.NewConnector(logger, cfg.Settings) default: - err = fmerrors.New("market: unsupported driver " + driver.String()) + err = merrors.InvalidArgument("market: unsupported driver "+driver.String(), "driver") } if err != nil { - return nil, fmerrors.Wrap("market: build connector "+driver.String(), err) + return nil, merrors.InternalWrap(err, "market: build connector "+driver.String()) } connectors[driver] = conn } diff --git a/api/fx/ingestor/internal/metrics/server.go b/api/fx/ingestor/internal/metrics/server.go index b7405cf..12f04c5 100644 --- a/api/fx/ingestor/internal/metrics/server.go +++ b/api/fx/ingestor/internal/metrics/server.go @@ -8,12 +8,12 @@ import ( "time" "github.com/go-chi/chi/v5" + "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/tech/sendico/fx/ingestor/internal/config" - "github.com/tech/sendico/fx/ingestor/internal/fmerrors" "github.com/tech/sendico/pkg/api/routers" "github.com/tech/sendico/pkg/api/routers/health" + "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/mlogger" - "github.com/prometheus/client_golang/prometheus/promhttp" "go.uber.org/zap" ) @@ -30,7 +30,7 @@ type Server interface { func NewServer(logger mlogger.Logger, cfg *config.MetricsConfig) (Server, error) { if logger == nil { - return nil, fmerrors.New("metrics: logger is nil") + return nil, merrors.InvalidArgument("metrics: logger is nil") } if cfg == nil || !cfg.Enabled { logger.Debug("Metrics disabled; using noop server") diff --git a/api/fx/ingestor/internal/model/connector.go b/api/fx/ingestor/internal/model/connector.go index 7dc4f9c..7fff1f6 100644 --- a/api/fx/ingestor/internal/model/connector.go +++ b/api/fx/ingestor/internal/model/connector.go @@ -10,6 +10,7 @@ type Driver string const ( DriverBinance Driver = "BINANCE" DriverCoinGecko Driver = "COINGECKO" + DriverCBR Driver = "CBR" ) func (d Driver) String() string { diff --git a/api/fx/oracle/go.mod b/api/fx/oracle/go.mod index 5089e0a..64b70ba 100644 --- a/api/fx/oracle/go.mod +++ b/api/fx/oracle/go.mod @@ -47,8 +47,8 @@ require ( go.yaml.in/yaml/v2 v2.4.3 // indirect golang.org/x/crypto v0.45.0 // indirect golang.org/x/net v0.47.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect ) diff --git a/api/fx/oracle/go.sum b/api/fx/oracle/go.sum index d63f35a..4d80caf 100644 --- a/api/fx/oracle/go.sum +++ b/api/fx/oracle/go.sum @@ -187,24 +187,24 @@ golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= diff --git a/api/fx/storage/go.mod b/api/fx/storage/go.mod index 536b0a3..8027b12 100644 --- a/api/fx/storage/go.mod +++ b/api/fx/storage/go.mod @@ -26,7 +26,7 @@ require ( github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.45.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/text v0.32.0 // indirect google.golang.org/protobuf v1.36.10 // indirect ) diff --git a/api/fx/storage/go.sum b/api/fx/storage/go.sum index 2334065..eee8330 100644 --- a/api/fx/storage/go.sum +++ b/api/fx/storage/go.sum @@ -147,8 +147,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -162,8 +162,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= diff --git a/api/gateway/chain/go.mod b/api/gateway/chain/go.mod index ffb3ce9..a295b32 100644 --- a/api/gateway/chain/go.mod +++ b/api/gateway/chain/go.mod @@ -22,7 +22,7 @@ require ( require ( github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251119083800-2aa1d4cc79d7 // indirect + github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251208031133-be43a854e4be // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.24.4 // indirect github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect @@ -82,9 +82,9 @@ require ( golang.org/x/crypto v0.45.0 // indirect golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect golang.org/x/net v0.47.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect golang.org/x/time v0.14.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect ) diff --git a/api/gateway/chain/go.sum b/api/gateway/chain/go.sum index b02c189..5a9570f 100644 --- a/api/gateway/chain/go.sum +++ b/api/gateway/chain/go.sum @@ -6,8 +6,8 @@ github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251119083800-2aa1d4cc79d7 h1:uups37roJCTtR/BrJa0WoMrxt3rzgV+Qrj+TxYyJoAo= -github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251119083800-2aa1d4cc79d7/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251208031133-be43a854e4be h1:1LtMLkGIqE5IQZ7Vdh4zv8A6LECInKF86/fTVxKxYLE= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251208031133-be43a854e4be/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0= github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -333,8 +333,8 @@ golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -343,16 +343,16 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/api/gateway/mntx/go.mod b/api/gateway/mntx/go.mod index 6fc7a2a..4aecbf3 100644 --- a/api/gateway/mntx/go.mod +++ b/api/gateway/mntx/go.mod @@ -47,8 +47,8 @@ require ( go.yaml.in/yaml/v2 v2.4.3 // indirect golang.org/x/crypto v0.45.0 // indirect golang.org/x/net v0.47.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect ) diff --git a/api/gateway/mntx/go.sum b/api/gateway/mntx/go.sum index 79911b6..5b881ba 100644 --- a/api/gateway/mntx/go.sum +++ b/api/gateway/mntx/go.sum @@ -189,24 +189,24 @@ golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= diff --git a/api/ledger/go.mod b/api/ledger/go.mod index 6864509..24ab43e 100644 --- a/api/ledger/go.mod +++ b/api/ledger/go.mod @@ -48,8 +48,8 @@ require ( go.yaml.in/yaml/v2 v2.4.3 // indirect golang.org/x/crypto v0.45.0 // indirect golang.org/x/net v0.47.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect ) diff --git a/api/ledger/go.sum b/api/ledger/go.sum index 1f5a08d..51e42ef 100644 --- a/api/ledger/go.sum +++ b/api/ledger/go.sum @@ -189,24 +189,24 @@ golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= diff --git a/api/notification/go.mod b/api/notification/go.mod index 4f2032f..e89ca0b 100644 --- a/api/notification/go.mod +++ b/api/notification/go.mod @@ -14,7 +14,7 @@ require ( github.com/xhit/go-simple-mail/v2 v2.16.0 go.mongodb.org/mongo-driver v1.17.6 go.uber.org/zap v1.27.1 - golang.org/x/text v0.31.0 + golang.org/x/text v0.32.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -50,8 +50,8 @@ require ( go.yaml.in/yaml/v2 v2.4.3 // indirect golang.org/x/crypto v0.45.0 // indirect golang.org/x/net v0.47.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect google.golang.org/grpc v1.77.0 // indirect google.golang.org/protobuf v1.36.10 // indirect diff --git a/api/notification/go.sum b/api/notification/go.sum index d018b97..6d7f489 100644 --- a/api/notification/go.sum +++ b/api/notification/go.sum @@ -202,24 +202,24 @@ golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= diff --git a/api/payments/orchestrator/go.mod b/api/payments/orchestrator/go.mod index f36be07..55a9b77 100644 --- a/api/payments/orchestrator/go.mod +++ b/api/payments/orchestrator/go.mod @@ -56,8 +56,8 @@ require ( go.yaml.in/yaml/v2 v2.4.3 // indirect golang.org/x/crypto v0.45.0 // indirect golang.org/x/net v0.47.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect ) diff --git a/api/payments/orchestrator/go.sum b/api/payments/orchestrator/go.sum index e9646dc..f9d3e69 100644 --- a/api/payments/orchestrator/go.sum +++ b/api/payments/orchestrator/go.sum @@ -190,24 +190,24 @@ golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= diff --git a/api/pkg/go.mod b/api/pkg/go.mod index dde3a93..ebd34f4 100644 --- a/api/pkg/go.mod +++ b/api/pkg/go.mod @@ -89,9 +89,9 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect golang.org/x/net v0.47.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/api/pkg/go.sum b/api/pkg/go.sum index 7ed16f1..d705953 100644 --- a/api/pkg/go.sum +++ b/api/pkg/go.sum @@ -223,8 +223,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -240,8 +240,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= @@ -250,8 +250,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/api/server/go.mod b/api/server/go.mod index e9c07f7..f612b60 100644 --- a/api/server/go.mod +++ b/api/server/go.mod @@ -132,9 +132,9 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect golang.org/x/crypto v0.45.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect google.golang.org/grpc v1.77.0 // indirect ) diff --git a/api/server/go.sum b/api/server/go.sum index 1500397..1adc889 100644 --- a/api/server/go.sum +++ b/api/server/go.sum @@ -311,8 +311,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -328,8 +328,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= @@ -338,8 +338,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=