diff --git a/.golangci.yml b/.golangci.yml index 84bc57d0f0..2dc226cb44 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -102,6 +102,8 @@ linters: - perfsprint # malfunctions on embedded structs - typecheck + # magic numbers + - mnd fast: false issues: diff --git a/agents/contracts/bondingmanager/bondingmanager.metadata.go b/agents/contracts/bondingmanager/bondingmanager.metadata.go index 06ce38a981..06331f5e91 100644 --- a/agents/contracts/bondingmanager/bondingmanager.metadata.go +++ b/agents/contracts/bondingmanager/bondingmanager.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed bondingmanager.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/destination/destination.metadata.go b/agents/contracts/destination/destination.metadata.go index 0209375ff5..64a0b732c2 100644 --- a/agents/contracts/destination/destination.metadata.go +++ b/agents/contracts/destination/destination.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed destination.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/gasoracle/gasoracle.metadata.go b/agents/contracts/gasoracle/gasoracle.metadata.go index 081f1977d0..92b1022d65 100644 --- a/agents/contracts/gasoracle/gasoracle.metadata.go +++ b/agents/contracts/gasoracle/gasoracle.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed gasoracle.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/inbox/inbox.metadata.go b/agents/contracts/inbox/inbox.metadata.go index b90d16bd12..1557ceb5b7 100644 --- a/agents/contracts/inbox/inbox.metadata.go +++ b/agents/contracts/inbox/inbox.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed inbox.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/lightinbox/lightinbox.metadata.go b/agents/contracts/lightinbox/lightinbox.metadata.go index a9722b6bdc..9c1648d95c 100644 --- a/agents/contracts/lightinbox/lightinbox.metadata.go +++ b/agents/contracts/lightinbox/lightinbox.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed lightinbox.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/lightmanager/lightmanager.metadata.go b/agents/contracts/lightmanager/lightmanager.metadata.go index e3141d5a52..8fa140c212 100644 --- a/agents/contracts/lightmanager/lightmanager.metadata.go +++ b/agents/contracts/lightmanager/lightmanager.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed lightmanager.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/origin/origin.metadata.go b/agents/contracts/origin/origin.metadata.go index 1d06a22cc9..00b31c78e9 100644 --- a/agents/contracts/origin/origin.metadata.go +++ b/agents/contracts/origin/origin.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed origin.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/summit/summit.metadata.go b/agents/contracts/summit/summit.metadata.go index 7679ad4a1f..3f5c1f1986 100644 --- a/agents/contracts/summit/summit.metadata.go +++ b/agents/contracts/summit/summit.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed summit.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/test/attestationharness/attestationharness.metadata.go b/agents/contracts/test/attestationharness/attestationharness.metadata.go index c91a330848..f381147d36 100644 --- a/agents/contracts/test/attestationharness/attestationharness.metadata.go +++ b/agents/contracts/test/attestationharness/attestationharness.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed attestationharness.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/test/basemessageharness/basemessageharness.metadata.go b/agents/contracts/test/basemessageharness/basemessageharness.metadata.go index 97350b5e05..a9a173b0d0 100644 --- a/agents/contracts/test/basemessageharness/basemessageharness.metadata.go +++ b/agents/contracts/test/basemessageharness/basemessageharness.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed basemessageharness.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/test/bondingmanagerharness/bondingmanagerharness.metadata.go b/agents/contracts/test/bondingmanagerharness/bondingmanagerharness.metadata.go index dd4e19c56d..4ca0064d7d 100644 --- a/agents/contracts/test/bondingmanagerharness/bondingmanagerharness.metadata.go +++ b/agents/contracts/test/bondingmanagerharness/bondingmanagerharness.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed bondingmanagerharness.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/test/destinationharness/destinationharness.metadata.go b/agents/contracts/test/destinationharness/destinationharness.metadata.go index 9768fccf32..2b5b118309 100644 --- a/agents/contracts/test/destinationharness/destinationharness.metadata.go +++ b/agents/contracts/test/destinationharness/destinationharness.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed destinationharness.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/test/gasdata/gasdataharness.metadata.go b/agents/contracts/test/gasdata/gasdataharness.metadata.go index a176245a40..1a5b220c3b 100644 --- a/agents/contracts/test/gasdata/gasdataharness.metadata.go +++ b/agents/contracts/test/gasdata/gasdataharness.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed gasdataharness.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/test/headerharness/headerharness.metadata.go b/agents/contracts/test/headerharness/headerharness.metadata.go index 258a09f51c..cd955f0637 100644 --- a/agents/contracts/test/headerharness/headerharness.metadata.go +++ b/agents/contracts/test/headerharness/headerharness.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed headerharness.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/test/lightmanagerharness/lightmanagerharness.metadata.go b/agents/contracts/test/lightmanagerharness/lightmanagerharness.metadata.go index 0c1686d37b..8936d13192 100644 --- a/agents/contracts/test/lightmanagerharness/lightmanagerharness.metadata.go +++ b/agents/contracts/test/lightmanagerharness/lightmanagerharness.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed lightmanagerharness.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/test/messageharness/messageharness.metadata.go b/agents/contracts/test/messageharness/messageharness.metadata.go index 0a0dbff6eb..b56bb2fc7a 100644 --- a/agents/contracts/test/messageharness/messageharness.metadata.go +++ b/agents/contracts/test/messageharness/messageharness.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed messageharness.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/test/originharness/originharness.metadata.go b/agents/contracts/test/originharness/originharness.metadata.go index f706dd6ae5..69ed8acdaa 100644 --- a/agents/contracts/test/originharness/originharness.metadata.go +++ b/agents/contracts/test/originharness/originharness.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed originharness.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/test/pingpongclient/pingpongclient.metadata.go b/agents/contracts/test/pingpongclient/pingpongclient.metadata.go index 90c40e7882..0df52c50c0 100644 --- a/agents/contracts/test/pingpongclient/pingpongclient.metadata.go +++ b/agents/contracts/test/pingpongclient/pingpongclient.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed pingpongclient.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/test/receiptharness/receiptharness.metadata.go b/agents/contracts/test/receiptharness/receiptharness.metadata.go index 3284c5eb36..1472c020ed 100644 --- a/agents/contracts/test/receiptharness/receiptharness.metadata.go +++ b/agents/contracts/test/receiptharness/receiptharness.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed receiptharness.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/test/requestharness/requestharness.metadata.go b/agents/contracts/test/requestharness/requestharness.metadata.go index 20a81ae417..f82f9e0864 100644 --- a/agents/contracts/test/requestharness/requestharness.metadata.go +++ b/agents/contracts/test/requestharness/requestharness.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed requestharness.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/test/snapshotharness/snapshotharness.metadata.go b/agents/contracts/test/snapshotharness/snapshotharness.metadata.go index fb818c239c..40f13465dc 100644 --- a/agents/contracts/test/snapshotharness/snapshotharness.metadata.go +++ b/agents/contracts/test/snapshotharness/snapshotharness.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed snapshotharness.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/test/stateharness/stateharness.metadata.go b/agents/contracts/test/stateharness/stateharness.metadata.go index fcc5a56faf..f88c745f93 100644 --- a/agents/contracts/test/stateharness/stateharness.metadata.go +++ b/agents/contracts/test/stateharness/stateharness.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed stateharness.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/test/summitharness/summitharness.metadata.go b/agents/contracts/test/summitharness/summitharness.metadata.go index 9386ce800a..d0dfb83d09 100644 --- a/agents/contracts/test/summitharness/summitharness.metadata.go +++ b/agents/contracts/test/summitharness/summitharness.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed summitharness.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/test/testclient/testclient.metadata.go b/agents/contracts/test/testclient/testclient.metadata.go index 30348dbfda..f80b561e40 100644 --- a/agents/contracts/test/testclient/testclient.metadata.go +++ b/agents/contracts/test/testclient/testclient.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed testclient.contractinfo.json var rawContracts []byte diff --git a/agents/contracts/test/tipsharness/tipsharness.metadata.go b/agents/contracts/test/tipsharness/tipsharness.metadata.go index 2c8140bbcd..ee03709b65 100644 --- a/agents/contracts/test/tipsharness/tipsharness.metadata.go +++ b/agents/contracts/test/tipsharness/tipsharness.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed tipsharness.contractinfo.json var rawContracts []byte diff --git a/agents/testutil/agentstestcontract/agentstestcontract.metadata.go b/agents/testutil/agentstestcontract/agentstestcontract.metadata.go index 53d7ccb767..e7c4dc9c15 100644 --- a/agents/testutil/agentstestcontract/agentstestcontract.metadata.go +++ b/agents/testutil/agentstestcontract/agentstestcontract.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed agentstestcontract.contractinfo.json var rawContracts []byte diff --git a/contrib/opbot/botmd/botmd.go b/contrib/opbot/botmd/botmd.go index e23444f05e..32ce34bfae 100644 --- a/contrib/opbot/botmd/botmd.go +++ b/contrib/opbot/botmd/botmd.go @@ -11,11 +11,13 @@ import ( "github.com/synapsecns/sanguine/core/dbcommon" "github.com/synapsecns/sanguine/core/metrics" "github.com/synapsecns/sanguine/core/metrics/instrumentation/slackertrace" + experimentalLogger "github.com/synapsecns/sanguine/core/metrics/logger" signerConfig "github.com/synapsecns/sanguine/ethergo/signer/config" "github.com/synapsecns/sanguine/ethergo/signer/signer" "github.com/synapsecns/sanguine/ethergo/submitter" cctpSql "github.com/synapsecns/sanguine/services/cctp-relayer/db/sql" omnirpcClient "github.com/synapsecns/sanguine/services/omnirpc/client" + "github.com/uptrace/opentelemetry-go-extra/otelzap" "golang.org/x/sync/errgroup" ) @@ -30,15 +32,20 @@ type Bot struct { signer signer.Signer submitter submitter.TransactionSubmitter screener screenerClient.ScreenerClient + logger experimentalLogger.ExperimentalLogger } // NewBot creates a new bot server. func NewBot(handler metrics.Handler, cfg config.Config) *Bot { server := slacker.NewClient(cfg.SlackBotToken, cfg.SlackAppToken) + + sugaredLogger := otelzap.New(experimentalLogger.MakeZapLogger()).Sugar() + bot := Bot{ handler: handler, cfg: cfg, server: server, + logger: experimentalLogger.MakeWrappedSugaredLogger(sugaredLogger), } // you should be able to run opbot even without signoz. diff --git a/contrib/opbot/botmd/commands.go b/contrib/opbot/botmd/commands.go index 0e81aa948c..5dc95b8403 100644 --- a/contrib/opbot/botmd/commands.go +++ b/contrib/opbot/botmd/commands.go @@ -9,6 +9,7 @@ import ( "log" "math/big" "regexp" + "sort" "strings" "sync" "time" @@ -50,14 +51,16 @@ func (b *Bot) requiresSignoz(definition *slacker.CommandDefinition) *slacker.Com // TODO: add trace middleware. func (b *Bot) traceCommand() *slacker.CommandDefinition { return b.requiresSignoz(&slacker.CommandDefinition{ - Command: "trace ", + Command: "trace {tags} {order}", Description: "find a transaction in signoz", Examples: []string{ - "trace transaction_id:0x1234 serviceName:rfq", + "trace transaction_id:0x1234@serviceName:rfq", + "trace transaction_id:0x1234@serviceName:rfq a", + "trace transaction_id:0x1234@serviceName:rfq asc", }, Handler: func(ctx *slacker.CommandContext) { tags := stripLinks(ctx.Request().Param("tags")) - splitTags := strings.Split(tags, " ") + splitTags := strings.Split(tags, "@") if len(splitTags) == 0 { _, err := ctx.Response().Reply("please provide tags in a key:value format") if err != nil { @@ -82,6 +85,7 @@ func (b *Bot) traceCommand() *slacker.CommandDefinition { // search for the transaction res, err := b.signozClient.SearchTraces(ctx.Context(), signoz.Last3Hr, searchMap) if err != nil { + b.logger.Errorf(ctx.Context(), "error searching for the transaction: %v", err) _, err := ctx.Response().Reply("error searching for the transaction") if err != nil { log.Println(err) @@ -106,6 +110,14 @@ func (b *Bot) traceCommand() *slacker.CommandDefinition { return } + order := strings.ToLower(ctx.Request().Param("order")) + isAscending := order == "a" || order == "asc" + if isAscending { + sort.Slice(traceList, func(i, j int) bool { + return traceList[i].Timestamp.Before(traceList[j].Timestamp) + }) + } + slackBlocks := []slack.Block{slack.NewHeaderBlock(slack.NewTextBlockObject(slack.PlainTextType, fmt.Sprintf("Traces for %s", tags), false, false))} for _, results := range traceList { @@ -290,6 +302,7 @@ func (b *Bot) rfqRefund() *slacker.CommandDefinition { } } if err != nil { + b.logger.Errorf(ctx.Context(), "error fetching quote request: %v", err) _, err := ctx.Response().Reply("error fetching quote request") if err != nil { log.Println(err) @@ -338,19 +351,23 @@ func (b *Bot) rfqRefund() *slacker.CommandDefinition { err = retry.WithBackoff( ctx.Context(), func(ctx context.Context) error { - txHash, err = relClient.GetTxHashByNonce(ctx, &relapi.GetTxByNonceRequest{ - ChainID: rawRequest.OriginChainID, - Nonce: nonce, - }) + txHash, err = relClient.GetTxHashByNonce( + ctx, + &relapi.GetTxByNonceRequest{ + ChainID: rawRequest.OriginChainID, + Nonce: nonce, + }) if err != nil { + b.logger.Errorf(ctx, "error fetching quote request: %v", err) return fmt.Errorf("error fetching quote request: %w", err) } return nil }, - retry.WithMaxAttempts(3), - retry.WithMaxAttemptTime(15*time.Second), + retry.WithMaxAttempts(5), + retry.WithMaxAttemptTime(30*time.Second), ) if err != nil { + b.logger.Errorf(ctx.Context(), "error fetching quote request: %v", err) _, err := ctx.Response().Reply(fmt.Sprintf("error fetching explorer link to refund, but nonce is %d", nonce)) log.Printf("error fetching quote request: %v\n", err) return diff --git a/contrib/opbot/go.mod b/contrib/opbot/go.mod index e251c320ce..71ec290d1a 100644 --- a/contrib/opbot/go.mod +++ b/contrib/opbot/go.mod @@ -28,6 +28,7 @@ require ( github.com/synapsecns/sanguine/services/cctp-relayer v0.0.0-00010101000000-000000000000 github.com/synapsecns/sanguine/services/omnirpc v0.0.0-00010101000000-000000000000 github.com/synapsecns/sanguine/services/rfq v0.0.0-00010101000000-000000000000 + github.com/uptrace/opentelemetry-go-extra/otelzap v0.3.1 github.com/urfave/cli/v2 v2.27.2 golang.org/x/sync v0.8.0 gopkg.in/yaml.v2 v2.4.0 @@ -240,7 +241,6 @@ require ( github.com/uptrace/opentelemetry-go-extra/otelgorm v0.3.1 // indirect github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.1 // indirect github.com/uptrace/opentelemetry-go-extra/otelutil v0.3.1 // indirect - github.com/uptrace/opentelemetry-go-extra/otelzap v0.3.1 // indirect github.com/valyala/fastjson v1.6.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect diff --git a/core/go.mod b/core/go.mod index 4f6e5bffcd..1143e38c1d 100644 --- a/core/go.mod +++ b/core/go.mod @@ -63,6 +63,7 @@ require ( go.uber.org/multierr v1.11.0 go.uber.org/zap v1.27.0 golang.org/x/sync v0.8.0 + google.golang.org/grpc v1.64.0 gorm.io/driver/sqlite v1.5.6 gorm.io/gorm v1.25.10 k8s.io/apimachinery v0.29.3 @@ -189,7 +190,6 @@ require ( golang.org/x/tools v0.24.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect - google.golang.org/grpc v1.64.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/core/metrics/README.md b/core/metrics/README.md index 1289afe41e..5f60dfbfe6 100644 --- a/core/metrics/README.md +++ b/core/metrics/README.md @@ -12,7 +12,27 @@ There's also a `NAME_PREFIX` environment variable that will prefix all the metri ## OTLP -We do our best to support enviornment variables specified in the [Otel Spec](https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/) and have added a few of our own. Key ones to note are: +We do our best to support enviornment variables specified in the [Otel Spec](https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/) and the [OTLP Spec](https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/) and have added a few of our own. This was to allow for multiple exporter backends for traces, as otel clients only allow for one URL. The relevant multi exporter code is in `multiexporter.go`, and simply wraps multiple otel clients. + +The additional environment variables to note are: +| Enviornment Variable | Description | Default | +|------------------------------------------|-------------------------------------------|---------| +| `OTEL_EXPORTER_OTLP_ENDPOINT` | The endpoint for the primary OTLP exporter | None | +| `OTEL_EXPORTER_OTLP_ENDPOINT_1` | The endpoint for the first additional OTLP exporter | None | +| `OTEL_EXPORTER_OTLP_ENDPOINT_2` | The endpoint for the second additional OTLP exporter | None | +| `OTEL_EXPORTER_OTLP_ENDPOINT_3` | The endpoint for the third additional OTLP exporter | None | +| ... | Additional endpoints can be added by incrementing the number | None | +| `OTEL_EXPORTER_OTLP_TRANSPORT` | The transport protocol for the primary OTLP exporter | `http` | +| `OTEL_EXPORTER_OTLP_TRANSPORT_1` | The transport protocol for the first additional OTLP exporter | `http` | +| `OTEL_EXPORTER_OTLP_TRANSPORT_2` | The transport protocol for the second additional OTLP exporter | `http` | +| `OTEL_EXPORTER_OTLP_TRANSPORT_3` | The transport protocol for the third additional OTLP exporter | `http` | +| ... | Additional transports can be specified by incrementing the number | `http` | + +You can do the same thing for `OTEL_EXPORTER_OTLP_SECURE_MODE` and `OTEL_EXPORTER_OTLP_HEADERS` + + + +Note: The OTLP exporter endpoints and transports can be specified for multiple exporters by using incrementing numbers (1, 2, 3, etc.) in the environment variable names. This allows for configuration of multiple OTLP exporters. The primary exporter uses the base names without numbers. ## Jaeger diff --git a/core/metrics/export_test.go b/core/metrics/export_test.go new file mode 100644 index 0000000000..6a25651c4c --- /dev/null +++ b/core/metrics/export_test.go @@ -0,0 +1,6 @@ +package metrics + +// HeadersToMap converts a string of headers to a map. +func HeadersToMap(val string) map[string]string { + return headersToMap(val) +} diff --git a/core/metrics/multiexporter.go b/core/metrics/multiexporter.go new file mode 100644 index 0000000000..cbd59495e4 --- /dev/null +++ b/core/metrics/multiexporter.go @@ -0,0 +1,83 @@ +package metrics + +import ( + "context" + "fmt" + "go.uber.org/multierr" + "sync" + "time" + + "go.opentelemetry.io/otel/sdk/trace" +) + +// MultiExporter is an interface that allows exporting spans to multiple OTLP trace exporters. +type MultiExporter interface { + trace.SpanExporter + AddExporter(exporter trace.SpanExporter) +} + +type multiExporter struct { + exporters []trace.SpanExporter +} + +// NewMultiExporter creates a new multi exporter that forwards spans to multiple OTLP trace exporters. +// It takes in one or more trace.SpanExporter instances and ensures that spans are sent to all of them. +// This is useful when you need to send trace data to multiple backends or endpoints. +func NewMultiExporter(exporters ...trace.SpanExporter) MultiExporter { + return &multiExporter{ + exporters: exporters, + } +} + +const defaultTimeout = 30 * time.Second + +// ExportSpans exports a batch of spans. +func (m *multiExporter) ExportSpans(parentCtx context.Context, ss []trace.ReadOnlySpan) error { + return m.doParallel(parentCtx, func(ctx context.Context, exporter trace.SpanExporter) error { + return exporter.ExportSpans(ctx, ss) + }) +} + +func (m *multiExporter) doParallel(parentCtx context.Context, fn func(context.Context, trace.SpanExporter) error) error { + ctx, cancel := context.WithTimeout(parentCtx, defaultTimeout) + defer cancel() + + var wg sync.WaitGroup + var errors []error + var mu sync.Mutex + + wg.Add(len(m.exporters)) + for _, exporter := range m.exporters { + go func(exporter trace.SpanExporter) { + defer wg.Done() + err := fn(ctx, exporter) + if err != nil { + mu.Lock() + errors = append(errors, fmt.Errorf("error in doMultiple: %w", err)) + mu.Unlock() + } + }(exporter) + } + + wg.Wait() + if len(errors) > 0 { + // nolint: wrapcheck + return multierr.Combine(errors...) + } + + return nil +} + +// Shutdown notifies the exporter of a pending halt to operations. +func (m *multiExporter) Shutdown(ctx context.Context) error { + return m.doParallel(ctx, func(ctx context.Context, exporter trace.SpanExporter) error { + return exporter.Shutdown(ctx) + }) +} + +// AddExporter adds an exporter to the multi exporter. +func (m *multiExporter) AddExporter(exporter trace.SpanExporter) { + m.exporters = append(m.exporters, exporter) +} + +var _ trace.SpanExporter = &multiExporter{} diff --git a/core/metrics/multiexporter_test.go b/core/metrics/multiexporter_test.go new file mode 100644 index 0000000000..0c0d6f0310 --- /dev/null +++ b/core/metrics/multiexporter_test.go @@ -0,0 +1,45 @@ +package metrics_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/synapsecns/sanguine/core/metrics" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/sdk/trace/tracetest" +) + +func TestMultiExporter(t *testing.T) { + // Create in-memory exporters + exporter1 := tracetest.NewInMemoryExporter() + exporter2 := tracetest.NewInMemoryExporter() + + // Create multi-exporter + multiExporter := metrics.NewMultiExporter(exporter1, exporter2) + + // Create test spans + spans := []sdktrace.ReadOnlySpan{ + tracetest.SpanStub{}.Snapshot(), + tracetest.SpanStub{}.Snapshot(), + } + + // Test ExportSpans + err := multiExporter.ExportSpans(context.Background(), spans) + require.NoError(t, err) + + // Verify that spans were exported to both exporters + assert.Equal(t, 2, len(exporter1.GetSpans())) + assert.Equal(t, 2, len(exporter2.GetSpans())) + + // Test Shutdown + err = multiExporter.Shutdown(context.Background()) + require.NoError(t, err) + + // Verify that both exporters were shut down + // Note: InMemoryExporter doesn't have a Stopped() method, so we can't check this directly + // Instead, we can try to export spans again and check for an error + err = multiExporter.ExportSpans(context.Background(), spans) + assert.NoError(t, err, "Expected no error after shutdown") +} diff --git a/core/metrics/otlp.go b/core/metrics/otlp.go index f96f06dd43..c5e2a7c236 100644 --- a/core/metrics/otlp.go +++ b/core/metrics/otlp.go @@ -2,16 +2,18 @@ package metrics import ( "context" + "crypto/tls" "fmt" + "google.golang.org/grpc/credentials" + "strings" + "time" + "github.com/synapsecns/sanguine/core" "github.com/synapsecns/sanguine/core/config" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" tracesdk "go.opentelemetry.io/otel/sdk/trace" - "os" - "strings" - "time" ) type otlpHandler struct { @@ -28,23 +30,45 @@ func NewOTLPMetricsHandler(buildInfo config.BuildInfo) Handler { } func (n *otlpHandler) Start(ctx context.Context) (err error) { - var client otlptrace.Client - transport := transportFromString(core.GetEnv(otlpTransportEnv, otlpTransportGRPC.String())) - switch transport { - case otlpTransportHTTP: - client = otlptracehttp.NewClient() - case otlpTransportGRPC: - client = otlptracegrpc.NewClient() - default: - return fmt.Errorf("unknown transport type: %s", os.Getenv(otlpTransportEnv)) - } + var exporters []tracesdk.SpanExporter - exporter, err := otlptrace.New(ctx, client) + primaryExporter, err := makeOTLPExporter(ctx, "") if err != nil { - return fmt.Errorf("failed to create otlp exporter: %w", err) + return fmt.Errorf("could not create default client: %w", err) + } + exporters = append(exporters, primaryExporter) + + // Loop to create additional exporters + for i := 1; ; i++ { + envSuffix := fmt.Sprintf("_%d", i) + // if this is empty we can assume no config exists at all. + endpointEnv := otelEndpointEnv + envSuffix + + // no more transports to add. + if !core.HasEnv(endpointEnv) { + break + } + + exporter, err := makeOTLPExporter(ctx, envSuffix) + if err != nil { + return fmt.Errorf("could not create exporter %d: %w", i, err) + } + + exporters = append(exporters, exporter) } - n.baseHandler = newBaseHandler(n.buildInfo, tracesdk.WithBatcher(exporter, tracesdk.WithMaxQueueSize(1000000), tracesdk.WithMaxExportBatchSize(2000)), tracesdk.WithSampler(tracesdk.AlwaysSample())) + // create the multi-exporter with all the exporters + multiExporter := NewMultiExporter(exporters...) + + n.baseHandler = newBaseHandler( + n.buildInfo, + tracesdk.WithBatcher( + multiExporter, + tracesdk.WithMaxQueueSize(defaultMaxQueueSize), + tracesdk.WithMaxExportBatchSize(defaultMaxExportBatch), + ), + tracesdk.WithSampler(tracesdk.AlwaysSample()), + ) // start the new parent err = n.baseHandler.Start(ctx) @@ -90,7 +114,10 @@ func handleShutdown(ctx context.Context, provider *tracesdk.TracerProvider) { } const ( - otlpTransportEnv = "OTEL_EXPORTER_OTLP_TRANSPORT" + otelEndpointEnv = "OTEL_EXPORTER_OTLP_ENDPOINT" + otelTransportEnv = "OTEL_EXPORTER_OTLP_TRANSPORT" + otelInsecureEvn = "OTEL_EXPORTER_OTLP_SECURE_MODE" + otelHeadersEnv = "OTEL_EXPORTER_OTLP_HEADERS" ) //go:generate go run golang.org/x/tools/cmd/stringer -type=otlpTransportType -linecomment @@ -101,6 +128,146 @@ const ( otlpTransportGRPC // grpc ) +// getEnvSuffix returns the value of an environment variable with a suffix. +func getEnvSuffix(env, suffix, defaultRet string) string { + return core.GetEnv(makeEnv(env, suffix), defaultRet) +} + +func makeEnv(env, suffix string) string { + return env + suffix +} + +// makeOTLPTrace creates a new OTLP client based on the transport type and url. +func makeOTLPExporter(ctx context.Context, envSuffix string) (*otlptrace.Exporter, error) { + transport := transportFromString(getEnvSuffix(otelTransportEnv, envSuffix, otlpTransportGRPC.String())) + url := getEnvSuffix(otelEndpointEnv, envSuffix, "") + secure := core.GetEnvBool(makeEnv(otelInsecureEvn, envSuffix), false) + headers := getEnvSuffix(otelHeadersEnv, envSuffix, "") + + isCorrect := envSuffix != "" + + if isCorrect != secure { + return nil, fmt.Errorf("could not create exporter: secure mode is not set correctly") + } + + // I spent about 2 hours trying to figure out why this was failing to no avail. I'm going to leave it as is for now. + // My best guess is the issue is around the tsl config. + // Should you attempt to fix this and fail, please increment the counter above, although I send my umost encouragement. + if secure && transport == otlpTransportHTTP { + return nil, fmt.Errorf("could not create exporter: http transport does not support secure mode") + } + + oteltraceClient, err := buildClientFromTransport( + transport, + withURL(url), + // defaults to true + withSecure(secure), + withHeaders(headers), + ) + if err != nil { + return nil, fmt.Errorf("could not create client from transport: %w", err) + } + + exporter, err := otlptrace.New(ctx, oteltraceClient) + if err != nil { + return nil, fmt.Errorf("ocould not create client: %w", err) + } + return exporter, nil +} + +// buildClientFromTransport creates a new OTLP client based on the transport type and url. +func buildClientFromTransport(transport otlpTransportType, options ...Option) (otlptrace.Client, error) { + to := transportOptions{} + for _, option := range options { + if err := option(&to); err != nil { + return nil, fmt.Errorf("could not apply option: %w", err) + } + } + + // TODO: make sure url is validated + + switch transport { + case otlpTransportHTTP: + return otlptracehttp.NewClient(to.httpOptions...), nil + case otlpTransportGRPC: + return otlptracegrpc.NewClient(to.grpcOptions...), nil + default: + return nil, fmt.Errorf("unknown transport type: %s", transport.String()) + } +} + +type transportOptions struct { + // httpOptions are the options for the http transport. + httpOptions []otlptracehttp.Option + // grpcOptions are the options for the grpc transport. + grpcOptions []otlptracegrpc.Option +} + +// Option Each option appends the correspond option for both http and grpc options. +// only one will be used in creating the actual client. +type Option func(*transportOptions) error + +func withURL(url string) Option { + return func(o *transportOptions) error { + o.httpOptions = append(o.httpOptions, otlptracehttp.WithEndpointURL(url)) + o.grpcOptions = append(o.grpcOptions, otlptracegrpc.WithEndpointURL(url)) + + return nil + } +} + +func withSecure(secure bool) Option { + return func(o *transportOptions) error { + if secure { + tlsCreds := credentials.NewClientTLSFromCert(nil, "") + // note: you do not need to specify the tls creds for http, this happens automatically when https:// is used as the protocol scheme. + o.grpcOptions = append(o.grpcOptions, otlptracegrpc.WithTLSCredentials(tlsCreds)) + + tlsConfig := &tls.Config{ + MinVersion: tls.VersionTLS12, + // RootCAs is nil, which means the default system root CAs are used + } + o.httpOptions = append(o.httpOptions, otlptracehttp.WithTLSClientConfig(tlsConfig)) + } else { + o.httpOptions = append(o.httpOptions, otlptracehttp.WithInsecure()) + o.grpcOptions = append(o.grpcOptions, otlptracegrpc.WithInsecure()) + } + + return nil + } +} + +func withHeaders(headers string) Option { + return func(o *transportOptions) error { + realHeaders := headersToMap(headers) + o.httpOptions = append(o.httpOptions, otlptracehttp.WithHeaders(realHeaders)) + o.grpcOptions = append(o.grpcOptions, otlptracegrpc.WithHeaders(realHeaders)) + return nil + } +} + +func headersToMap(input string) map[string]string { + // Initialize the map + result := make(map[string]string) + + // Split the input string by comma to get key=value pairs + pairs := strings.Split(input, ",") + + // Iterate over each pair + for _, pair := range pairs { + // Split each pair by '=' to get the key and value + kv := strings.SplitN(pair, "=", 2) + if len(kv) == 2 { + key := kv[0] + value := kv[1] + // Add the key and value to the map + result[key] = value + } + } + + return result +} + // transportFromString converts a string to a transport type. // Defaults to http if the string is not recognized. func transportFromString(transport string) otlpTransportType { @@ -114,3 +281,8 @@ func transportFromString(transport string) otlpTransportType { // (see uber's go stye guide for details) return otlpTransportType(0) } + +const ( + defaultMaxQueueSize = 1000000 + defaultMaxExportBatch = 2000 +) diff --git a/core/metrics/otlp_test.go b/core/metrics/otlp_test.go new file mode 100644 index 0000000000..55a127836a --- /dev/null +++ b/core/metrics/otlp_test.go @@ -0,0 +1,71 @@ +package metrics_test + +import ( + "github.com/synapsecns/sanguine/core/metrics" + "reflect" + "testing" +) + +func TestHeadersToMap(t *testing.T) { + tests := []struct { + name string + input string + expected map[string]string + }{ + { + name: "basic input", + input: "key1=value1,key2=value2", + expected: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + }, + { + name: "empty input", + input: "", + expected: map[string]string{}, + }, + { + name: "input with extra spaces", + input: "key1 = value1 , key2= value2 ", + expected: map[string]string{ + "key1 ": " value1 ", + " key2": " value2 ", + }, + }, + { + name: "input with missing value", + input: "key1=value1,key2=", + expected: map[string]string{ + "key1": "value1", + "key2": "", + }, + }, + { + name: "input with missing key", + input: "=value1,key2=value2", + expected: map[string]string{ + "": "value1", + "key2": "value2", + }, + }, + { + name: "input with multiple equal signs", + input: "key1=value1=extra,key2=value2", + expected: map[string]string{ + "key1": "value1=extra", + "key2": "value2", + }, + }, + } + + for i := range tests { + tt := tests[i] + t.Run(tt.name, func(t *testing.T) { + result := metrics.HeadersToMap(tt.input) + if !reflect.DeepEqual(result, tt.expected) { + t.Errorf("HeadersToMap(%v) = %v; want %v", tt.input, result, tt.expected) + } + }) + } +} diff --git a/core/metrics/rookout.go b/core/metrics/rookout.go index 88b72a611d..5ed6302ccb 100644 --- a/core/metrics/rookout.go +++ b/core/metrics/rookout.go @@ -3,12 +3,13 @@ package metrics import ( + "os" + rookout "github.com/Rookout/GoSDK" "github.com/Rookout/GoSDK/pkg/config" "github.com/synapsecns/sanguine/core" synconfig "github.com/synapsecns/sanguine/core/config" "github.com/synapsecns/sanguine/core/metrics/internal" - "os" ) // DefaultGitRepo is the default git repo for sanguine. diff --git a/docs/bridge/docs/rfq/API/API.md b/docs/bridge/docs/rfq/API/API.md index 392f91e1c0..dfbc77ffe3 100644 --- a/docs/bridge/docs/rfq/API/API.md +++ b/docs/bridge/docs/rfq/API/API.md @@ -25,6 +25,16 @@ The RFQ API is a RESTful API that allows users to post quotes and read quotes. T Only Solvers should be writing to the API, end-users need only read from the `/quotes` endpoint. +**API Version Changes** + +An http response header "X-Api-Version" will be returned on each call response. + +Any systems that integrate with the API should use this header to detect version changes and perform appropriate follow-up actions & alerts. + +Upon a version change, [versions.go](https://github.com/synapsecns/sanguine/blob/master/services/rfq/api/rest/versions.go) can be referred to for further detail on the version including deprecation alerts, etc. + +Please note, while Synapse may choose to take additional steps to alert & advise on API changes through other communication channels, it will remain the responsibility of the API users & integrators to set up their own detection & notifications of version changes as they use these endpoints. Likewise, it will be their responsibility review the versions.go file, to research & understand how any changes may affect their integration, and to implement any necessary adjustments resulting from the API changes. + **Authentication** In accordance with [EIP-191](https://eips.ethereum.org/EIPS/eip-191), the RFQ API requires a signature to be sent with each request. The signature should be generated by the user's wallet and should be a valid signature of the message `rfq-api` with the user's private key. The signature should be sent in the `Authorization` header of the request. We provide a client stub/example implementation in go [here](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq@v0.13.3/api/client). diff --git a/ethergo/examples/contracttests/counter/counter.metadata.go b/ethergo/examples/contracttests/counter/counter.metadata.go index dfe086cc6e..a3423de604 100644 --- a/ethergo/examples/contracttests/counter/counter.metadata.go +++ b/ethergo/examples/contracttests/counter/counter.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed counter.contractinfo.json var rawContracts []byte diff --git a/ethergo/parser/abiutil/internal/testsignature.metadata.go b/ethergo/parser/abiutil/internal/testsignature.metadata.go index e7ca6dad61..39118e45cc 100644 --- a/ethergo/parser/abiutil/internal/testsignature.metadata.go +++ b/ethergo/parser/abiutil/internal/testsignature.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed testsignature.contractinfo.json var rawContracts []byte diff --git a/packages/contracts-rfq/CHANGELOG.md b/packages/contracts-rfq/CHANGELOG.md index e6bb7bcdb9..4c55dc04d6 100644 --- a/packages/contracts-rfq/CHANGELOG.md +++ b/packages/contracts-rfq/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.3.0](https://github.com/synapsecns/sanguine/compare/FastBridge@0.2.14...FastBridge@0.3.0) (2024-09-10) + + +### Features + +* **contracts-rfq:** Multicall target abstraction [SLT-134] ([#3078](https://github.com/synapsecns/sanguine/issues/3078)) ([100324f](https://github.com/synapsecns/sanguine/commit/100324f269f77f73fc10913d0162676f5f918996)) + + + + + ## [0.2.14](https://github.com/synapsecns/sanguine/compare/FastBridge@0.2.13...FastBridge@0.2.14) (2024-07-29) **Note:** Version bump only for package FastBridge diff --git a/packages/contracts-rfq/contracts/interfaces/IMulticallTarget.sol b/packages/contracts-rfq/contracts/interfaces/IMulticallTarget.sol new file mode 100644 index 0000000000..1f48e59609 --- /dev/null +++ b/packages/contracts-rfq/contracts/interfaces/IMulticallTarget.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/// @notice Interface for a contract that can be called multiple times by the same caller. Inspired by MulticallV3: +/// https://github.com/mds1/multicall/blob/master/src/Multicall3.sol +interface IMulticallTarget { + struct Result { + bool success; + bytes returnData; + } + + function multicallNoResults(bytes[] calldata data, bool ignoreReverts) external; + function multicallWithResults( + bytes[] calldata data, + bool ignoreReverts + ) + external + returns (Result[] memory results); +} diff --git a/packages/contracts-rfq/contracts/utils/MulticallTarget.sol b/packages/contracts-rfq/contracts/utils/MulticallTarget.sol new file mode 100644 index 0000000000..bed9266c33 --- /dev/null +++ b/packages/contracts-rfq/contracts/utils/MulticallTarget.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IMulticallTarget} from "../interfaces/IMulticallTarget.sol"; + +// solhint-disable avoid-low-level-calls +/// @notice Template for a contract that supports batched calls (preserving the msg.sender). +/// Only calls with zero msg.value could be batched. +abstract contract MulticallTarget is IMulticallTarget { + error MulticallTarget__UndeterminedRevert(); + + /// @notice Perform a batched call to this contract, preserving the msg.sender. + /// The return data from each call is discarded. + /// @dev The method is non-payable, so only calls with `msg.value == 0` could be batched. + /// It's possible to ignore the reverts from the calls by setting the `ignoreReverts` flag. + /// Otherwise, the whole batch call will be reverted with the original revert reason. + /// @param data List of abi-encoded calldata for the calls to perform. + /// @param ignoreReverts Whether to ignore the revert errors from the calls. + function multicallNoResults(bytes[] calldata data, bool ignoreReverts) external { + for (uint256 i = 0; i < data.length; ++i) { + // We perform a delegate call to ourself to preserve the msg.sender. This is identical to `msg.sender` + // calling the functions directly one by one, therefore doesn't add any security risks. + // Note: msg.value is also preserved when doing a delegate call, but this function is not payable, + // so it's always 0 and not a security risk. + (bool success, bytes memory result) = address(this).delegatecall(data[i]); + if (!success && !ignoreReverts) { + _bubbleRevert(result); + } + } + } + + /// @notice Perform a batched call to this contract, preserving the msg.sender. + /// The return data from each call is preserved. + /// @dev The method is non-payable, so only calls with `msg.value == 0` could be batched. + /// It's possible to ignore the reverts from the calls by setting the `ignoreReverts` flag. + /// Otherwise, the whole batch call will be reverted with the original revert reason. + /// @param data List of abi-encoded calldata for the calls to perform. + /// @param ignoreReverts Whether to ignore the revert errors from the calls. + /// @return results List of results from the calls: `(success, returnData)`. + function multicallWithResults( + bytes[] calldata data, + bool ignoreReverts + ) + external + returns (Result[] memory results) + { + results = new Result[](data.length); + for (uint256 i = 0; i < data.length; ++i) { + // We perform a delegate call to ourself to preserve the msg.sender. This is identical to `msg.sender` + // calling the functions directly one by one, therefore doesn't add any security risks. + // Note: msg.value is also preserved when doing a delegate call, but this function is not payable, + // so it's always 0 and not a security risk. + (results[i].success, results[i].returnData) = address(this).delegatecall(data[i]); + if (!results[i].success && !ignoreReverts) { + _bubbleRevert(results[i].returnData); + } + } + } + + /// @dev Bubbles the revert message from the underlying call. + /// Note: preserves the same custom error or revert string, if one was used. + /// Source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.0.2/contracts/utils/Address.sol#L143-L158 + function _bubbleRevert(bytes memory returnData) internal pure { + // Look for revert reason and bubble it up if present + if (returnData.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + // solhint-disable-next-line no-inline-assembly + assembly { + let returndata_size := mload(returnData) + revert(add(32, returnData), returndata_size) + } + } else { + revert MulticallTarget__UndeterminedRevert(); + } + } +} diff --git a/packages/contracts-rfq/package.json b/packages/contracts-rfq/package.json index 15c7279950..d778482a30 100644 --- a/packages/contracts-rfq/package.json +++ b/packages/contracts-rfq/package.json @@ -1,7 +1,7 @@ { "name": "FastBridge", "license": "UNLICENSED", - "version": "0.2.14", + "version": "0.3.0", "description": "FastBridge contracts.", "private": true, "files": [ diff --git a/packages/contracts-rfq/test/MulticallTarget.t.sol b/packages/contracts-rfq/test/MulticallTarget.t.sol new file mode 100644 index 0000000000..1c12bcaca5 --- /dev/null +++ b/packages/contracts-rfq/test/MulticallTarget.t.sol @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IMulticallTarget} from "../contracts/interfaces/IMulticallTarget.sol"; +import {MulticallTargetHarness, MulticallTarget} from "./harnesses/MulticallTargetHarness.sol"; + +import {Test} from "forge-std/Test.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract MulticallTargetTest is Test { + MulticallTargetHarness public harness; + + address public caller = makeAddr("Caller"); + + function setUp() public { + harness = new MulticallTargetHarness(); + harness.setAddressField(address(1)); + harness.setUintField(2); + } + + function getEncodedStringRevertMessage() internal view returns (bytes memory) { + return abi.encodeWithSignature("Error(string)", harness.REVERT_MESSAGE()); + } + + function getMsgSenderData() internal view returns (bytes[] memory) { + return toArray( + abi.encodeCall(harness.setAddressField, (address(1234))), + abi.encodeCall(harness.addressField, ()), + abi.encodeCall(harness.setMsgSenderAsAddressField, ()), + abi.encodeCall(harness.addressField, ()) + ); + } + + function getMsgSenderResults() internal view returns (IMulticallTarget.Result[] memory) { + return toArray( + IMulticallTarget.Result(true, abi.encode(address(1234))), + IMulticallTarget.Result(true, abi.encode(address(1234))), + IMulticallTarget.Result(true, abi.encode(caller)), + IMulticallTarget.Result(true, abi.encode(caller)) + ); + } + + function getNoRevertsData() internal view returns (bytes[] memory) { + return toArray( + abi.encodeCall(harness.addressField, ()), + abi.encodeCall(harness.setAddressField, (address(1234))), + abi.encodeCall(harness.setUintField, (42)), + abi.encodeCall(harness.setAddressField, (address(0xDEADBEAF))) + ); + } + + function getNoRevertsResults() internal pure returns (IMulticallTarget.Result[] memory) { + return toArray( + IMulticallTarget.Result(true, abi.encode(address(1))), + IMulticallTarget.Result(true, abi.encode(address(1234))), + IMulticallTarget.Result(true, abi.encode(42)), + IMulticallTarget.Result(true, abi.encode(address(0xDEADBEAF))) + ); + } + + function getCustomErrorRevertData() internal view returns (bytes[] memory) { + return toArray( + abi.encodeCall(harness.setAddressField, (address(1234))), + abi.encodeCall(harness.setUintField, (42)), + abi.encodeCall(harness.customErrorRevert, ()), + abi.encodeCall(harness.setAddressField, (address(0xDEADBEAF))) + ); + } + + function getCustomErrorRevertResults() internal pure returns (IMulticallTarget.Result[] memory) { + return toArray( + IMulticallTarget.Result(true, abi.encode(address(1234))), + IMulticallTarget.Result(true, abi.encode(42)), + IMulticallTarget.Result(false, abi.encodeWithSelector(MulticallTargetHarness.CustomError.selector)), + IMulticallTarget.Result(true, abi.encode(address(0xDEADBEAF))) + ); + } + + function getStringRevertData() internal view returns (bytes[] memory) { + return toArray( + abi.encodeCall(harness.setAddressField, (address(1234))), + abi.encodeCall(harness.setUintField, (42)), + abi.encodeCall(harness.revertingFunction, ()), + abi.encodeCall(harness.setAddressField, (address(0xDEADBEAF))) + ); + } + + function getStringRevertResults() internal view returns (IMulticallTarget.Result[] memory) { + return toArray( + IMulticallTarget.Result(true, abi.encode(address(1234))), + IMulticallTarget.Result(true, abi.encode(42)), + IMulticallTarget.Result(false, abi.encodeWithSignature("Error(string)", harness.REVERT_MESSAGE())), + IMulticallTarget.Result(true, abi.encode(address(0xDEADBEAF))) + ); + } + + function getUndeterminedRevertData() internal view returns (bytes[] memory) { + return toArray( + abi.encodeCall(harness.setAddressField, (address(1234))), + abi.encodeCall(harness.setUintField, (42)), + abi.encodeCall(harness.undeterminedRevert, ()), + abi.encodeCall(harness.setAddressField, (address(0xDEADBEAF))) + ); + } + + function getUndeterminedRevertResults() internal pure returns (IMulticallTarget.Result[] memory) { + return toArray( + IMulticallTarget.Result(true, abi.encode(address(1234))), + IMulticallTarget.Result(true, abi.encode(42)), + IMulticallTarget.Result(false, ""), + IMulticallTarget.Result(true, abi.encode(address(0xDEADBEAF))) + ); + } + + // ══════════════════════════════════════════ MULTICALL (NO RESULTS) ═══════════════════════════════════════════════ + + function test_multicallNoResults_ignoreReverts_noReverts() public { + bytes[] memory data = getNoRevertsData(); + harness.multicallNoResults({data: data, ignoreReverts: true}); + + assertEq(harness.addressField(), address(0xDEADBEAF)); + assertEq(harness.uintField(), 42); + } + + function test_multicallNoResults_ignoreReverts_withMsgSender() public { + bytes[] memory data = getMsgSenderData(); + vm.prank(caller); + harness.multicallNoResults({data: data, ignoreReverts: true}); + + assertEq(harness.addressField(), caller); + assertEq(harness.uintField(), 2); + } + + function test_multicallNoResults_ignoreReverts_withCustomErrorRevert() public { + bytes[] memory data = getCustomErrorRevertData(); + harness.multicallNoResults({data: data, ignoreReverts: true}); + + assertEq(harness.addressField(), address(0xDEADBEAF)); + assertEq(harness.uintField(), 42); + } + + function test_multicallNoResults_ignoreReverts_withStringRevert() public { + bytes[] memory data = getStringRevertData(); + harness.multicallNoResults({data: data, ignoreReverts: true}); + + assertEq(harness.addressField(), address(0xDEADBEAF)); + assertEq(harness.uintField(), 42); + } + + function test_multicallNoResults_ignoreReverts_withUndeterminedRevert() public { + bytes[] memory data = getUndeterminedRevertData(); + harness.multicallNoResults({data: data, ignoreReverts: true}); + + assertEq(harness.addressField(), address(0xDEADBEAF)); + assertEq(harness.uintField(), 42); + } + + function test_multicallNoResults_dontIgnoreReverts_noReverts() public { + bytes[] memory data = getNoRevertsData(); + harness.multicallNoResults({data: data, ignoreReverts: false}); + + assertEq(harness.addressField(), address(0xDEADBEAF)); + assertEq(harness.uintField(), 42); + } + + function test_multicallNoResults_dontIgnoreReverts_withMsgSender() public { + bytes[] memory data = getMsgSenderData(); + vm.prank(caller); + harness.multicallNoResults({data: data, ignoreReverts: false}); + + assertEq(harness.addressField(), caller); + assertEq(harness.uintField(), 2); + } + + function test_multicallNoResults_dontIgnoreReverts_withCustomErrorRevert() public { + bytes[] memory data = getCustomErrorRevertData(); + vm.expectRevert(MulticallTargetHarness.CustomError.selector); + harness.multicallNoResults({data: data, ignoreReverts: false}); + } + + function test_multicallNoResults_dontIgnoreReverts_withStringRevert() public { + bytes[] memory data = getStringRevertData(); + string memory revertMessage = harness.REVERT_MESSAGE(); + vm.expectRevert(bytes(revertMessage)); + harness.multicallNoResults({data: data, ignoreReverts: false}); + } + + function test_multicallNoResults_dontIgnoreReverts_withUndeterminedRevert() public { + bytes[] memory data = getUndeterminedRevertData(); + vm.expectRevert(MulticallTarget.MulticallTarget__UndeterminedRevert.selector); + harness.multicallNoResults({data: data, ignoreReverts: false}); + } + + // ═════════════════════════════════════════ MULTICALL (WITH RESULTS) ══════════════════════════════════════════════ + + function test_multicallWithResults_ignoreReverts_noReverts() public { + bytes[] memory data = getNoRevertsData(); + IMulticallTarget.Result[] memory results = harness.multicallWithResults({data: data, ignoreReverts: true}); + + assertEq(results, getNoRevertsResults()); + assertEq(harness.addressField(), address(0xDEADBEAF)); + assertEq(harness.uintField(), 42); + } + + function test_multicallWithResults_ignoreReverts_withMsgSender() public { + bytes[] memory data = getMsgSenderData(); + vm.prank(caller); + IMulticallTarget.Result[] memory results = harness.multicallWithResults({data: data, ignoreReverts: true}); + + assertEq(results, getMsgSenderResults()); + assertEq(harness.uintField(), 2); + assertEq(harness.addressField(), caller); + } + + function test_multicallWithResults_ignoreReverts_withCustomErrorRevert() public { + bytes[] memory data = getCustomErrorRevertData(); + IMulticallTarget.Result[] memory results = harness.multicallWithResults({data: data, ignoreReverts: true}); + + assertEq(results, getCustomErrorRevertResults()); + assertEq(harness.addressField(), address(0xDEADBEAF)); + assertEq(harness.uintField(), 42); + } + + function test_multicallWithResults_ignoreReverts_withStringRevert() public { + bytes[] memory data = getStringRevertData(); + IMulticallTarget.Result[] memory results = harness.multicallWithResults({data: data, ignoreReverts: true}); + + assertEq(results, getStringRevertResults()); + assertEq(harness.addressField(), address(0xDEADBEAF)); + assertEq(harness.uintField(), 42); + } + + function test_multicallWithResults_ignoreReverts_withUndeterminedRevert() public { + bytes[] memory data = getUndeterminedRevertData(); + IMulticallTarget.Result[] memory results = harness.multicallWithResults({data: data, ignoreReverts: true}); + + assertEq(results, getUndeterminedRevertResults()); + assertEq(harness.addressField(), address(0xDEADBEAF)); + assertEq(harness.uintField(), 42); + } + + function test_multicallWithResults_dontIgnoreReverts_noReverts() public { + bytes[] memory data = getNoRevertsData(); + IMulticallTarget.Result[] memory results = harness.multicallWithResults({data: data, ignoreReverts: false}); + + assertEq(results, getNoRevertsResults()); + assertEq(harness.addressField(), address(0xDEADBEAF)); + assertEq(harness.uintField(), 42); + } + + function test_multicallWithResults_dontIgnoreReverts_withMsgSender() public { + bytes[] memory data = getMsgSenderData(); + vm.prank(caller); + IMulticallTarget.Result[] memory results = harness.multicallWithResults({data: data, ignoreReverts: false}); + + assertEq(results, getMsgSenderResults()); + assertEq(harness.addressField(), caller); + assertEq(harness.uintField(), 2); + } + + function test_multicallWithResults_dontIgnoreReverts_withCustomErrorRevert() public { + bytes[] memory data = getCustomErrorRevertData(); + vm.expectRevert(MulticallTargetHarness.CustomError.selector); + harness.multicallWithResults({data: data, ignoreReverts: false}); + } + + function test_multicallWithResults_dontIgnoreReverts_withStringRevert() public { + bytes[] memory data = getStringRevertData(); + string memory revertMessage = harness.REVERT_MESSAGE(); + vm.expectRevert(bytes(revertMessage)); + harness.multicallWithResults({data: data, ignoreReverts: false}); + } + + function test_multicallWithResults_dontIgnoreReverts_withUndeterminedRevert() public { + bytes[] memory data = getUndeterminedRevertData(); + vm.expectRevert(MulticallTarget.MulticallTarget__UndeterminedRevert.selector); + harness.multicallWithResults({data: data, ignoreReverts: false}); + } + + // ══════════════════════════════════════════════════ VIEW ════════════════════════════════════════════════════ + + function assertEq(IMulticallTarget.Result memory a, IMulticallTarget.Result memory b) internal pure { + assertEq(a.success, b.success); + assertEq(a.returnData, b.returnData); + } + + function assertEq(IMulticallTarget.Result[] memory a, IMulticallTarget.Result[] memory b) internal pure { + assertEq(a.length, b.length); + for (uint256 i = 0; i < a.length; i++) { + assertEq(a[i], b[i]); + } + } + + function toArray( + bytes memory a, + bytes memory b, + bytes memory c, + bytes memory d + ) + internal + pure + returns (bytes[] memory arr) + { + arr = new bytes[](4); + arr[0] = a; + arr[1] = b; + arr[2] = c; + arr[3] = d; + } + + function toArray( + IMulticallTarget.Result memory a, + IMulticallTarget.Result memory b, + IMulticallTarget.Result memory c, + IMulticallTarget.Result memory d + ) + internal + pure + returns (IMulticallTarget.Result[] memory arr) + { + arr = new IMulticallTarget.Result[](4); + arr[0] = a; + arr[1] = b; + arr[2] = c; + arr[3] = d; + } +} diff --git a/packages/contracts-rfq/test/harnesses/MulticallTargetHarness.sol b/packages/contracts-rfq/test/harnesses/MulticallTargetHarness.sol new file mode 100644 index 0000000000..5819dbf3fc --- /dev/null +++ b/packages/contracts-rfq/test/harnesses/MulticallTargetHarness.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {MulticallTarget} from "../../contracts/utils/MulticallTarget.sol"; + +contract MulticallTargetHarness is MulticallTarget { + address public addressField; + uint256 public uintField; + + string public constant REVERT_MESSAGE = "gm, this is a revert message"; + + error CustomError(); + + function setMsgSenderAsAddressField() external returns (address) { + addressField = msg.sender; + return addressField; + } + + function setAddressField(address _addressField) external returns (address) { + addressField = _addressField; + return addressField; + } + + function setUintField(uint256 _uintField) external returns (uint256) { + uintField = _uintField; + return uintField; + } + + function customErrorRevert() external pure { + revert CustomError(); + } + + function revertingFunction() external pure { + revert(REVERT_MESSAGE); + } + + function undeterminedRevert() external pure { + // solhint-disable-next-line no-inline-assembly + assembly { + revert(0, 0) + } + } +} diff --git a/packages/rest-api/.babelrc b/packages/rest-api/.babelrc new file mode 100644 index 0000000000..3313ff9ef0 --- /dev/null +++ b/packages/rest-api/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@babel/preset-env", "@babel/preset-typescript"] +} diff --git a/packages/rest-api/.eslintrc.js b/packages/rest-api/.eslintrc.js index bfd2057be8..e17fcaafc9 100644 --- a/packages/rest-api/.eslintrc.js +++ b/packages/rest-api/.eslintrc.js @@ -1,3 +1,11 @@ module.exports = { extends: '../../.eslintrc.js', + overrides: [ + { + files: ['jest.config.js'], + rules: { + 'prettier/prettier': 'off', + }, + }, + ], } diff --git a/packages/rest-api/CHANGELOG.md b/packages/rest-api/CHANGELOG.md index 1665e37772..7f8eb1eec9 100644 --- a/packages/rest-api/CHANGELOG.md +++ b/packages/rest-api/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.0.74](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.0.73...@synapsecns/rest-api@1.0.74) (2024-09-16) + +**Note:** Version bump only for package @synapsecns/rest-api + + + + + +## [1.0.73](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.0.72...@synapsecns/rest-api@1.0.73) (2024-09-12) + +**Note:** Version bump only for package @synapsecns/rest-api + + + + + ## [1.0.72](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.0.71...@synapsecns/rest-api@1.0.72) (2024-09-04) diff --git a/packages/rest-api/jest.config.js b/packages/rest-api/jest.config.js new file mode 100644 index 0000000000..23f6f815f5 --- /dev/null +++ b/packages/rest-api/jest.config.js @@ -0,0 +1,10 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + roots: ['/src'], + transform: { + '^.+\\.(ts|tsx)$': 'babel-jest', + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], + moduleDirectories: ['node_modules', 'src'], +} diff --git a/packages/rest-api/package.json b/packages/rest-api/package.json index ccc3784f7b..2dad80fe96 100644 --- a/packages/rest-api/package.json +++ b/packages/rest-api/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/rest-api", - "version": "1.0.72", + "version": "1.0.74", "private": "true", "engines": { "node": ">=18.17.0" @@ -8,13 +8,14 @@ "scripts": { "build": "tsc", "build:go": " ", + "build:slither": " ", + "dev": "nodemon --watch src --exec ts-node src/app.ts", "start": "node dist/app.js", "lint:fix": "eslint src/**/*.ts --fix", "lint:check": "eslint . --max-warnings=0", "ci:lint": "npm run lint:check", - "test": "tsdx test", - "test:coverage": "echo 'No tests defined.'", - "postinstall": "npm run build" + "test": "jest", + "test:coverage": "echo 'No tests defined.'" }, "dependencies": { "@ethersproject/bignumber": "^5.7.0", @@ -24,15 +25,23 @@ "bignumber": "^1.1.0", "ethers": "5.7.2", "express": "^4.18.2", + "express-validator": "^7.2.0", "lodash": "^4.17.21", - "supertest": "^6.3.3" + "supertest": "^6.3.3", + "typescript": "^4.8.3" }, "description": "A node.js project exposing a rest api for synapse sdk quotes", "devDependencies": { + "@babel/core": "^7.25.2", + "@babel/preset-env": "^7.25.4", + "@babel/preset-typescript": "^7.24.7", + "@types/jest": "^29.5.13", "@types/supertest": "^6.0.2", "concurrently": "^8.2.0", + "jest": "^29.7.0", "lodash": "^4.17.21", "nodemon": "^3.0.1", - "typescript": "^4.8.3" + "ts-jest": "^29.2.5", + "ts-node": "^10.9.2" } } diff --git a/packages/rest-api/src/app.ts b/packages/rest-api/src/app.ts index 104db349ff..cf03670416 100644 --- a/packages/rest-api/src/app.ts +++ b/packages/rest-api/src/app.ts @@ -1,496 +1,13 @@ -import { JsonRpcProvider } from '@ethersproject/providers' -import { SynapseSDK } from '@synapsecns/sdk-router' -import { BigNumber } from '@ethersproject/bignumber' -import { formatUnits, parseUnits } from '@ethersproject/units' import express from 'express' -import * as tokensList from './constants/bridgeable' -import { CHAINS_ARRAY } from './constants/chains' -import { Chain } from './types' -import { BRIDGE_MAP } from './constants/bridgeMap' +import routes from './routes' -const chains: Chain[] = CHAINS_ARRAY -const findTokenInfo = (chain: string, tokenSymbol: string) => { - const chainData = BRIDGE_MAP[chain] - if (!chainData) { - return null - } - for (const tokenAddress in chainData) { - if (chainData[tokenAddress].symbol === tokenSymbol) { - return { - address: tokenAddress, - decimals: chainData[tokenAddress].decimals, - } - } - } - - return null -} - -const tokenHtml = Object.values(tokensList) - .map((token: any) => { - const chainList = Object.entries(token.addresses) - .map( - ([chainId, tokenAddress]) => - `
  • Chain Id: ${chainId}, Address: ${tokenAddress}
  • ` - ) - .join('') - - return ` - Token: ${token.symbol}
    - ${chainList} -
    - ` - }) - .join('') - -// Set up Synapse SDK -const providers = [] -const chainIds = [] - -for (const chain of chains) { - providers.push(new JsonRpcProvider(chain.rpcUrls.primary)) - chainIds.push(chain.id) -} -// Define the sdk -const Synapse = new SynapseSDK(chainIds, providers) - -// Set up express server const app = express() const port = process.env.PORT || 3000 -//Intro Message for UI -app.get('/', (_req, res) => { - res.send( - ` -

    Welcome to the Synapse Rest API for swap and bridge quotes

    -
    -

    Available Chains

    - -

    Available Tokens (symbols to use)

    - ${tokenHtml}` - ) -}) - -app.get('/tokenList', (_req, res) => { - res.send(tokensList) -}) - -//Swap Quote get request -app.get('/swap', async (req, res) => { - try { - // Access query params - const query = req.query - - // Chain - const chainId = String(query.chain) - - // Symbols - const fromTokenSymbol = String(query.fromToken) - const toTokenSymbol = String(query.toToken) - - const fromTokenInfo = findTokenInfo(chainId, fromTokenSymbol) - const toTokenInfo = findTokenInfo(chainId, toTokenSymbol) - - if (!fromTokenInfo || !toTokenInfo) { - res.send( - ` -

    Invalid Params

    -
    - Ensure that your request matches the following format: /swap?chain=1&fromToken=USDC&toToken=DAI&amount=100 -

    Available Tokens (symbols to use)

    - ${tokenHtml}` - ) - return - } - // Get Token Addresses and decimals (new) - const fromTokenAddress = fromTokenInfo.address - const toTokenAddress = toTokenInfo.address - const fromTokenDecimals = fromTokenInfo.decimals - const toTokenDecimals = toTokenInfo.decimals - - if ( - !fromTokenAddress || - !toTokenAddress || - !fromTokenDecimals || - !toTokenDecimals - ) { - res.send( - ` -

    Invalid Params

    -
    - Ensure that your request matches the following format: /swap?chain=1&fromToken=USDC&toToken=DAI&amount=100 -

    Available Tokens (symbols to use)

    - ${tokenHtml}` - ) - return - } - - // Handle amount - const amount = parseUnits( - query.amount.toString(), - fromTokenDecimals - ).toString() - // Send request w/Synapse SDK - Synapse.swapQuote( - Number(chainId), - fromTokenAddress, - toTokenAddress, - BigNumber.from(amount) - ) - .then((resp) => { - const payload: any = resp - payload.maxAmountOutStr = formatBNToString( - resp.maxAmountOut, - toTokenDecimals - ) - res.json(payload) - }) - .catch((err) => { - res.send( - ` -

    Invalid Request

    - ${err} -
    - Ensure that your request matches the following format: /swap?chain=1&fromToken=USDC&toToken=DAI&amount=100 -

    Available Tokens (symbols to use)

    - ${tokenHtml}` - ) - }) - } catch (err) { - res.status(400).send({ - message: 'Invalid request', - error: err.message, - }) - } -}) - -//BridgeQuote get request -app.get('/bridge', async (req, res) => { - // Access query params - try { - const query = req.query - - // Chains - const fromChain = String(query.fromChain) - const toChain = String(query.toChain) - - // Symbols - const fromTokenSymbol = String(query.fromToken) - const toTokenSymbol = String(query.toToken) - - // Get Token Info - const fromTokenInfo = findTokenInfo(fromChain, fromTokenSymbol) - const toTokenInfo = findTokenInfo(toChain, toTokenSymbol) - - if (!fromTokenInfo || !toTokenInfo) { - res.send( - ` -

    Invalid Params

    -
    - Ensure that your request matches the following format: /bridge?fromChain=1&toChain=42161&fromToken=USDC&toToken=USDC&amount=1000000 - -

    Available Tokens (symbols to use)

    - ${tokenHtml}` - ) - return - } - // Get Token Addresses and decimals (new) - const fromTokenAddress = fromTokenInfo.address - const toTokenAddress = toTokenInfo.address - const fromTokenDecimals = fromTokenInfo.decimals - const toTokenDecimals = toTokenInfo.decimals - - if ( - !fromTokenAddress || - !toTokenAddress || - !fromTokenDecimals || - !toTokenDecimals - ) { - res.send( - ` -

    Invalid Request

    -
    - Ensure that your request matches the following format: /bridge?fromChain=1&toChain=42161&fromToken=USDC&toToken=USDC&amount=1000000 -

    Available Tokens (symbols to use)

    - ${tokenHtml}` - ) - return - } - - // Handle amount - const amount = parseUnits( - query.amount.toString(), - fromTokenDecimals - ).toString() - // Send request w/Synapse SDK - Synapse.allBridgeQuotes( - Number(fromChain), - Number(toChain), - fromTokenAddress, - toTokenAddress, - BigNumber.from(amount) - ) - .then((resp) => { - const payload = resp.map((quote) => ({ - ...quote, - maxAmountOutStr: formatBNToString( - quote.maxAmountOut, - toTokenDecimals - ), - bridgeFeeFormatted: formatBNToString( - quote.feeAmount, - toTokenDecimals - ), - })) - res.json(payload) - }) - .catch((err) => { - res.send( - ` -

    Invalid Request

    - ${err} -
    - Ensure that your request matches the following format: /bridge?fromChain=1&toChain=42161&fromToken=USDC&toToken=USDC&amount=1000000 -

    Available Tokens (symbols to use)

    - ${tokenHtml}` - ) - }) - } catch (err) { - res.status(400).send({ - message: 'Invalid request', - error: err.message, - }) - } -}) - -// Beginning of txInfo functions --> These return the txInfo to actually bridge -app.get('/swapTxInfo', async (req, res) => { - try { - // Access query params - const query = req.query - - // Chain - const chainId = String(query.chain) - - // Symbols - const fromTokenSymbol = String(query.fromToken) - const toTokenSymbol = String(query.toToken) - - // Get Token Info - const fromTokenInfo = findTokenInfo(chainId, fromTokenSymbol) - const toTokenInfo = findTokenInfo(chainId, toTokenSymbol) - - if (!fromTokenInfo || !toTokenInfo) { - res.send( - ` -

    Invalid Params

    -
    - Ensure that your request matches the following format: /bridge?fromChain=1&toChain=42161&fromToken=USDC&toToken=USDC&amount=1000000 - -

    Available Tokens (symbols to use)

    - ${tokenHtml}` - ) - return - } - // Get Token Addresses and decimals (new) - const fromTokenAddress = fromTokenInfo.address - const toTokenAddress = toTokenInfo.address - const fromTokenDecimals = fromTokenInfo.decimals - const toTokenDecimals = toTokenInfo.decimals - - if ( - !fromTokenAddress || - !toTokenAddress || - !fromTokenDecimals || - !toTokenDecimals - ) { - res.send( - ` -

    Invalid Params

    -
    - Ensure that your request matches the following format: /swap?chain=1&fromToken=USDC&toToken=DAI&amount=100 -

    Available Tokens (symbols to use)

    - ${tokenHtml}` - ) - return - } - - // Handle amount - const amount = parseUnits( - query.amount.toString(), - fromTokenDecimals - ).toString() - - // Send request w/Synapse SDK - Synapse.swapQuote( - Number(chainId), - fromTokenAddress, - toTokenAddress, - BigNumber.from(amount) - ) - .then((resp) => { - Synapse.swap( - Number(chainId), - fromTokenAddress, - toTokenAddress, - BigNumber.from(amount), - resp.query - ).then((txInfo) => { - res.json(txInfo) - }) - }) - .catch((err) => { - res.send( - ` -

    Invalid Request

    - ${err} -
    - Ensure that your request matches the following format: /swapTxInfo?chain=1&fromToken=USDC&toToken=DAI&amount=100 -

    Available Tokens (symbols to use)

    - ${tokenHtml}` - ) - }) - } catch (err) { - res.status(400).send({ - message: 'Invalid request', - error: err.message, - }) - } -}) - -//BridgeTxInfo -app.get('/bridgeTxInfo', async (req, res) => { - try { - // Access query params - const query = req.query - - // Chains - const fromChain = String(query.fromChain) - const toChain = String(query.toChain) - - // Symbols - const fromTokenSymbol = String(query.fromToken) - const toTokenSymbol = String(query.toToken) - - // Get Token Info - const fromTokenInfo = findTokenInfo(fromChain, fromTokenSymbol) - const toTokenInfo = findTokenInfo(toChain, toTokenSymbol) - - if (!fromTokenInfo || !toTokenInfo) { - res.send( - ` -

    Invalid Params

    -
    - Ensure that your request matches the following format: /bridge?fromChain=1&toChain=42161&fromToken=USDC&toToken=USDC&amount=1000000 - -

    Available Tokens (symbols to use)

    - ${tokenHtml}` - ) - return - } - // Get Token Addresses and decimals (new) - const fromTokenAddress = fromTokenInfo.address - const toTokenAddress = toTokenInfo.address - const fromTokenDecimals = fromTokenInfo.decimals - const toTokenDecimals = toTokenInfo.decimals - - //Get to Address on destination chain - const destAddress = String(query.destAddress) - - // Handle invalid params (either token symbols or chainIDs) - // TODO: add error handling for missing params - if ( - !fromTokenAddress || - !toTokenAddress || - !fromTokenDecimals || - !toTokenDecimals - ) { - res.send( - ` -

    Invalid Request

    -
    - Ensure that your request matches the following format: /bridgeTxInfo?fromChain=1&toChain=42161&fromToken=USDC&toToken=USDC&amount=1000000&destAddress=0xcc78d2f004c9de9694ff6a9bbdee4793d30f3842 -

    Available Tokens (symbols to use)

    - ${tokenHtml}` - ) - return - } - - // Handle amount - const amount = parseUnits( - query.amount.toString(), - fromTokenDecimals - ).toString() - - // Send request with Synapse SDK - Synapse.allBridgeQuotes( - Number(fromChain), - Number(toChain), - fromTokenAddress, - toTokenAddress, - BigNumber.from(amount) - ) - .then(async (resp) => { - const txInfoArray = await Promise.all( - resp.map(async (quote) => { - const txInfo = await Synapse.bridge( - destAddress, - quote.routerAddress, - Number(fromChain), - Number(toChain), - fromTokenAddress, - BigNumber.from(amount), - quote.originQuery, - quote.destQuery - ) - return txInfo - }) - ) - res.json(txInfoArray) - }) - .catch((err) => { - // TODO: do a better return here - res.send( - ` -

    Invalid Request

    - ${err} -
    - Ensure that your request matches the following format: /bridgeTxInfo?fromChain=1&toChain=42161&fromToken=USDC&toToken=USDC&amount=1000000&destAddress=0xcc78d2f004c9de9694ff6a9bbdee4793d30f3842 -

    Available Tokens (symbols to use)

    - ${tokenHtml}` - ) - }) - } catch (err) { - res.status(400).send({ - message: 'Invalid request', - error: err.message, - }) - } -}) +app.use(express.json()) +app.use('/', routes) export const server = app.listen(port, () => { console.log(`Server listening at ${port}`) }) - -const formatBNToString = ( - bn: BigNumber, - nativePrecison: number, - decimalPlaces = 18 -) => { - const fullPrecision = formatUnits(bn, nativePrecison) - const decimalIdx = fullPrecision.indexOf('.') - - if (decimalPlaces === undefined || decimalIdx === -1) { - return fullPrecision - } else { - const rawNumber = Number(fullPrecision) - - if (rawNumber === 0) { - return rawNumber.toFixed(1) - } - return rawNumber.toString() - } -} diff --git a/packages/rest-api/src/constants/index.ts b/packages/rest-api/src/constants/index.ts new file mode 100644 index 0000000000..716c1fd365 --- /dev/null +++ b/packages/rest-api/src/constants/index.ts @@ -0,0 +1,5 @@ +export const VALID_BRIDGE_MODULES = [ + 'SynapseBridge', + 'SynapseCCTP', + 'SynapseRFQ', +] diff --git a/packages/rest-api/src/controllers/bridgeController.ts b/packages/rest-api/src/controllers/bridgeController.ts new file mode 100644 index 0000000000..aeead94c52 --- /dev/null +++ b/packages/rest-api/src/controllers/bridgeController.ts @@ -0,0 +1,44 @@ +import { validationResult } from 'express-validator' +import { parseUnits } from '@ethersproject/units' + +import { formatBNToString } from '../utils/formatBNToString' +import { Synapse } from '../services/synapseService' + +export const bridgeController = async (req, res) => { + const errors = validationResult(req) + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }) + } + try { + const { fromChain, toChain, amount } = req.query + const fromTokenInfo = res.locals.tokenInfo.fromToken + const toTokenInfo = res.locals.tokenInfo.toToken + + const amountInWei = parseUnits(amount.toString(), fromTokenInfo.decimals) + + const resp = await Synapse.allBridgeQuotes( + Number(fromChain), + Number(toChain), + fromTokenInfo.address, + toTokenInfo.address, + amountInWei + ) + const payload = resp.map((quote) => ({ + ...quote, + maxAmountOutStr: formatBNToString( + quote.maxAmountOut, + toTokenInfo.decimals + ), + bridgeFeeFormatted: formatBNToString( + quote.feeAmount, + toTokenInfo.decimals + ), + })) + res.json(payload) + } catch (err) { + res.status(500).json({ + error: 'An unexpected error occurred in /bridge. Please try again later.', + details: err.message, + }) + } +} diff --git a/packages/rest-api/src/controllers/bridgeTxInfoController.ts b/packages/rest-api/src/controllers/bridgeTxInfoController.ts new file mode 100644 index 0000000000..3f75330dee --- /dev/null +++ b/packages/rest-api/src/controllers/bridgeTxInfoController.ts @@ -0,0 +1,50 @@ +import { validationResult } from 'express-validator' +import { parseUnits } from '@ethersproject/units' + +import { Synapse } from '../services/synapseService' + +export const bridgeTxInfoController = async (req, res) => { + const errors = validationResult(req) + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }) + } + + try { + const { fromChain, toChain, amount, destAddress } = req.query + const fromTokenInfo = res.locals.tokenInfo.fromToken + const toTokenInfo = res.locals.tokenInfo.toToken + + const amountInWei = parseUnits(amount.toString(), fromTokenInfo.decimals) + + const quotes = await Synapse.allBridgeQuotes( + Number(fromChain), + Number(toChain), + fromTokenInfo.address, + toTokenInfo.address, + amountInWei + ) + + const txInfoArray = await Promise.all( + quotes.map(async (quote) => { + const txInfo = await Synapse.bridge( + destAddress, + quote.routerAddress, + Number(fromChain), + Number(toChain), + fromTokenInfo.address, + amountInWei, + quote.originQuery, + quote.destQuery + ) + return txInfo + }) + ) + res.json(txInfoArray) + } catch (err) { + res.status(500).json({ + error: + 'An unexpected error occurred in /bridgeTxInfo. Please try again later.', + details: err.message, + }) + } +} diff --git a/packages/rest-api/src/controllers/getBridgeTxStatusController.ts b/packages/rest-api/src/controllers/getBridgeTxStatusController.ts new file mode 100644 index 0000000000..1234545082 --- /dev/null +++ b/packages/rest-api/src/controllers/getBridgeTxStatusController.ts @@ -0,0 +1,79 @@ +import { validationResult } from 'express-validator' +import { ethers } from 'ethers' + +import { Synapse } from '../services/synapseService' +import { getTokenDecimals } from '../utils/getTokenDecimals' + +export const getBridgeTxStatusController = async (req, res) => { + const errors = validationResult(req) + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }) + } + + try { + const { destChainId, bridgeModule, synapseTxId } = req.query + + const status = await Synapse.getBridgeTxStatus( + Number(destChainId), + bridgeModule, + synapseTxId + ) + + if (status) { + const txIdWithout0x = synapseTxId.startsWith('0x') + ? synapseTxId.slice(2) + : synapseTxId + const graphqlEndpoint = 'https://explorer.omnirpc.io/graphql' + const graphqlQuery = ` + { + bridgeTransactions(useMv: true, kappa: "${txIdWithout0x}") { + toInfo { + chainID + address + txnHash + value + USDValue + tokenSymbol + tokenAddress + formattedTime + } + } + } + ` + + const graphqlResponse = await fetch(graphqlEndpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ query: graphqlQuery }), + }) + + const graphqlData = await graphqlResponse.json() + const toInfo = graphqlData.data.bridgeTransactions[0]?.toInfo + + if (toInfo) { + const { tokenAddress, value, ...restToInfo } = toInfo + + const tokenDecimals = getTokenDecimals(toInfo.chainID, tokenAddress) + const formattedValue = ethers.utils.formatUnits(value, tokenDecimals) + + res.json({ + status, + toInfo: { + ...restToInfo, + formattedValue: `${formattedValue}`, + }, + }) + } else { + res.json({ status, toInfo: null }) + } + } else { + res.json({ status }) + } + } catch (err) { + res.status(500).json({ + error: + 'An unexpected error occurred in /getBridgeTxStatus. Please try again later.', + details: err.message, + }) + } +} diff --git a/packages/rest-api/src/controllers/getDestinationTxController.ts b/packages/rest-api/src/controllers/getDestinationTxController.ts new file mode 100644 index 0000000000..4e17b08f66 --- /dev/null +++ b/packages/rest-api/src/controllers/getDestinationTxController.ts @@ -0,0 +1,72 @@ +import { validationResult } from 'express-validator' +import { ethers } from 'ethers' + +import { getTokenDecimals } from '../utils/getTokenDecimals' + +export const getDestinationTxController = async (req, res) => { + const errors = validationResult(req) + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }) + } + + try { + const { originChainId, txHash } = req.query + + const graphqlEndpoint = 'https://explorer.omnirpc.io/graphql' + const graphqlQuery = ` + { + bridgeTransactions( + useMv: true + chainIDFrom: ${originChainId} + txnHash: "${txHash}" + ) { + toInfo { + chainID + address + txnHash + value + USDValue + tokenSymbol + tokenAddress + blockNumber + formattedTime + } + } + } + ` + + const graphqlResponse = await fetch(graphqlEndpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ query: graphqlQuery }), + }) + + const graphqlData = await graphqlResponse.json() + const toInfo = graphqlData.data.bridgeTransactions[0]?.toInfo || null + + if (toInfo) { + const { tokenAddress, value, ...restToInfo } = toInfo + + const tokenDecimals = getTokenDecimals(toInfo.chainID, tokenAddress) + const formattedValue = ethers.utils.formatUnits(value, tokenDecimals) + + res.json({ + status: 'completed', + toInfo: { + ...restToInfo, + formattedValue: `${formattedValue}`, + }, + }) + } else { + res.json({ status: 'pending', toInfo: null }) + } + } catch (err) { + res.status(500).json({ + error: + 'An unexpected error occurred in /getDestinationTx. Please try again later.', + details: err.message, + }) + } +} diff --git a/packages/rest-api/src/controllers/getSynapseTxIdController.ts b/packages/rest-api/src/controllers/getSynapseTxIdController.ts new file mode 100644 index 0000000000..c9977ca73d --- /dev/null +++ b/packages/rest-api/src/controllers/getSynapseTxIdController.ts @@ -0,0 +1,27 @@ +import { validationResult } from 'express-validator' + +import { Synapse } from '../services/synapseService' + +export const getSynapseTxIdController = async (req, res) => { + const errors = validationResult(req) + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }) + } + + try { + const { originChainId, bridgeModule, txHash } = req.query + + const synapseTxId = await Synapse.getSynapseTxId( + Number(originChainId), + bridgeModule, + txHash + ) + res.json({ synapseTxId }) + } catch (err) { + res.status(500).json({ + error: + 'An unexpected error occurred in /getSynapseTxId. Please try again later.', + details: err.message, + }) + } +} diff --git a/packages/rest-api/src/controllers/indexController.ts b/packages/rest-api/src/controllers/indexController.ts new file mode 100644 index 0000000000..f0c8380ddf --- /dev/null +++ b/packages/rest-api/src/controllers/indexController.ts @@ -0,0 +1,30 @@ +import * as tokensList from '../constants/bridgeable' +import { CHAINS_ARRAY } from '../constants/chains' + +export const indexController = async (_req, res) => { + try { + const tokensWithChains = Object.values(tokensList).map((token: any) => ({ + symbol: token.symbol, + chains: Object.entries(token.addresses).map( + ([chainId, tokenAddress]) => ({ + chainId, + address: tokenAddress, + }) + ), + })) + + res.json({ + message: 'Welcome to the Synapse REST API for swap and bridge quotes', + availableChains: CHAINS_ARRAY.map((chain) => ({ + name: chain.name, + id: chain.id, + })), + availableTokens: tokensWithChains, + }) + } catch (err) { + res.status(500).json({ + error: 'An unexpected error occurred in /. Please try again later.', + details: err.message, + }) + } +} diff --git a/packages/rest-api/src/controllers/swapController.ts b/packages/rest-api/src/controllers/swapController.ts new file mode 100644 index 0000000000..53d28412bb --- /dev/null +++ b/packages/rest-api/src/controllers/swapController.ts @@ -0,0 +1,33 @@ +import { validationResult } from 'express-validator' +import { formatUnits, parseUnits } from '@ethersproject/units' + +import { Synapse } from '../services/synapseService' + +export const swapController = async (req, res) => { + const errors = validationResult(req) + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }) + } + try { + const { chain, amount } = req.query + const fromTokenInfo = res.locals.tokenInfo.fromToken + const toTokenInfo = res.locals.tokenInfo.toToken + + const amountInWei = parseUnits(amount.toString(), fromTokenInfo.decimals) + const quote = await Synapse.swapQuote( + Number(chain), + fromTokenInfo.address, + toTokenInfo.address, + amountInWei + ) + res.json({ + maxAmountOut: formatUnits(quote.maxAmountOut, toTokenInfo.decimals), + ...quote, + }) + } catch (err) { + res.status(500).json({ + error: 'An unexpected error occurred in /swap. Please try again later.', + details: err.message, + }) + } +} diff --git a/packages/rest-api/src/controllers/swapTxInfoController.ts b/packages/rest-api/src/controllers/swapTxInfoController.ts new file mode 100644 index 0000000000..9577d3ceea --- /dev/null +++ b/packages/rest-api/src/controllers/swapTxInfoController.ts @@ -0,0 +1,42 @@ +import { validationResult } from 'express-validator' +import { parseUnits } from '@ethersproject/units' + +import { Synapse } from '../services/synapseService' + +export const swapTxInfoController = async (req, res) => { + const errors = validationResult(req) + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }) + } + + try { + const { chain, amount } = req.query + const fromTokenInfo = res.locals.tokenInfo.fromToken + const toTokenInfo = res.locals.tokenInfo.toToken + + const amountInWei = parseUnits(amount.toString(), fromTokenInfo.decimals) + + const quote = await Synapse.swapQuote( + Number(chain), + fromTokenInfo.address, + toTokenInfo.address, + amountInWei + ) + + const txInfo = await Synapse.swap( + Number(chain), + fromTokenInfo.address, + toTokenInfo.address, + amountInWei, + quote.query + ) + + res.json(txInfo) + } catch (err) { + res.status(500).json({ + error: + 'An unexpected error occurred in /swapTxInfo. Please try again later.', + details: err.message, + }) + } +} diff --git a/packages/rest-api/src/controllers/tokenListController.ts b/packages/rest-api/src/controllers/tokenListController.ts new file mode 100644 index 0000000000..b6cc4d8e25 --- /dev/null +++ b/packages/rest-api/src/controllers/tokenListController.ts @@ -0,0 +1,5 @@ +import * as tokenList from '../constants/bridgeable' + +export const tokenListController = async (_req, res) => { + res.json(tokenList) +} diff --git a/packages/rest-api/src/middleware/showFirstValidationError.ts b/packages/rest-api/src/middleware/showFirstValidationError.ts new file mode 100644 index 0000000000..f37f8fcc02 --- /dev/null +++ b/packages/rest-api/src/middleware/showFirstValidationError.ts @@ -0,0 +1,24 @@ +import { Request, Response, NextFunction } from 'express' +import { validationResult } from 'express-validator' + +export const showFirstValidationError = ( + req: Request, + res: Response, + next: NextFunction +): void => { + const errors = validationResult(req) + if (!errors.isEmpty()) { + const firstError = errors.array({ onlyFirstError: true })[0] + + res.status(400).json({ + error: { + value: (firstError as any).value, + message: firstError.msg, + field: firstError.type === 'field' ? firstError.path : undefined, + location: (firstError as any).location, + }, + }) + return + } + next() +} diff --git a/packages/rest-api/src/routes/bridgeRoute.ts b/packages/rest-api/src/routes/bridgeRoute.ts new file mode 100644 index 0000000000..cb00696b5b --- /dev/null +++ b/packages/rest-api/src/routes/bridgeRoute.ts @@ -0,0 +1,34 @@ +import express from 'express' +import { check } from 'express-validator' + +import { CHAINS_ARRAY } from '../constants/chains' +import { validateTokens } from '../validations/validateTokens' +import { showFirstValidationError } from '../middleware/showFirstValidationError' +import { bridgeController } from '../controllers/bridgeController' + +const router = express.Router() + +router.get( + '/', + [ + check('fromChain') + .isNumeric() + .custom((value) => CHAINS_ARRAY.some((c) => c.id === Number(value))) + .withMessage('Unsupported fromChain') + .exists() + .withMessage('fromChain is required'), + check('toChain') + .isNumeric() + .custom((value) => CHAINS_ARRAY.some((c) => c.id === Number(value))) + .withMessage('Unsupported toChain') + .exists() + .withMessage('toChain is required'), + validateTokens('fromChain', 'fromToken', 'fromToken'), + validateTokens('toChain', 'toToken', 'toToken'), + check('amount').isNumeric(), + ], + showFirstValidationError, + bridgeController +) + +export default router diff --git a/packages/rest-api/src/routes/bridgeTxInfoRoute.ts b/packages/rest-api/src/routes/bridgeTxInfoRoute.ts new file mode 100644 index 0000000000..8929f66202 --- /dev/null +++ b/packages/rest-api/src/routes/bridgeTxInfoRoute.ts @@ -0,0 +1,35 @@ +import express from 'express' +import { check } from 'express-validator' + +import { CHAINS_ARRAY } from '../constants/chains' +import { validateTokens } from '../validations/validateTokens' +import { showFirstValidationError } from '../middleware/showFirstValidationError' +import { bridgeTxInfoController } from '../controllers/bridgeTxInfoController' + +const router = express.Router() + +router.get( + '/', + [ + check('fromChain') + .isNumeric() + .custom((value) => CHAINS_ARRAY.some((c) => c.id === Number(value))) + .withMessage('Unsupported fromChain') + .exists() + .withMessage('fromChain is required'), + check('toChain') + .isNumeric() + .custom((value) => CHAINS_ARRAY.some((c) => c.id === Number(value))) + .withMessage('Unsupported toChain') + .exists() + .withMessage('toChain is required'), + validateTokens('fromChain', 'fromToken', 'fromToken'), + validateTokens('toChain', 'toToken', 'toToken'), + check('amount').isNumeric(), + check('destAddress').isString(), + ], + showFirstValidationError, + bridgeTxInfoController +) + +export default router diff --git a/packages/rest-api/src/routes/getBridgeTxStatusRoute.ts b/packages/rest-api/src/routes/getBridgeTxStatusRoute.ts new file mode 100644 index 0000000000..cc72b8ec41 --- /dev/null +++ b/packages/rest-api/src/routes/getBridgeTxStatusRoute.ts @@ -0,0 +1,38 @@ +import express from 'express' +import { check } from 'express-validator' + +import { showFirstValidationError } from '../middleware/showFirstValidationError' +import { getBridgeTxStatusController } from '../controllers/getBridgeTxStatusController' +import { CHAINS_ARRAY } from '../constants/chains' +import { VALID_BRIDGE_MODULES } from '../constants' + +const router = express.Router() + +router.get( + '/', + [ + check('destChainId') + .isNumeric() + .custom((value) => CHAINS_ARRAY.some((c) => c.id === Number(value))) + .withMessage('Unsupported destChainId') + .exists() + .withMessage('destChainId is required'), + check('bridgeModule') + .isString() + .isIn(VALID_BRIDGE_MODULES) + .withMessage( + 'Invalid bridge module. Must be one of: ' + + VALID_BRIDGE_MODULES.join(', ') + ) + .exists() + .withMessage('bridgeModule is required'), + check('synapseTxId') + .isString() + .exists() + .withMessage('synapseTxId is required'), + ], + showFirstValidationError, + getBridgeTxStatusController +) + +export default router diff --git a/packages/rest-api/src/routes/getDestinationTxRoute.ts b/packages/rest-api/src/routes/getDestinationTxRoute.ts new file mode 100644 index 0000000000..5c15f6205a --- /dev/null +++ b/packages/rest-api/src/routes/getDestinationTxRoute.ts @@ -0,0 +1,22 @@ +import express from 'express' +import { check } from 'express-validator' + +import { showFirstValidationError } from '../middleware/showFirstValidationError' +import { getDestinationTxController } from '../controllers/getDestinationTxController' + +const router = express.Router() + +router.get( + '/', + [ + check('originChainId') + .isNumeric() + .exists() + .withMessage('originChainId is required'), + check('txHash').isString().exists().withMessage('txHash is required'), + ], + showFirstValidationError, + getDestinationTxController +) + +export default router diff --git a/packages/rest-api/src/routes/getSynapseTxIdRoute.ts b/packages/rest-api/src/routes/getSynapseTxIdRoute.ts new file mode 100644 index 0000000000..7ea582171c --- /dev/null +++ b/packages/rest-api/src/routes/getSynapseTxIdRoute.ts @@ -0,0 +1,32 @@ +import express from 'express' +import { check } from 'express-validator' + +import { showFirstValidationError } from '../middleware/showFirstValidationError' +import { getSynapseTxIdController } from '../controllers/getSynapseTxIdController' +import { VALID_BRIDGE_MODULES } from '../constants' + +const router = express.Router() + +router.get( + '/', + [ + check('originChainId') + .isNumeric() + .exists() + .withMessage('originChainId is required'), + check('bridgeModule') + .isString() + .isIn(VALID_BRIDGE_MODULES) + .withMessage( + 'Invalid bridge module. Must be one of: ' + + VALID_BRIDGE_MODULES.join(', ') + ) + .exists() + .withMessage('bridgeModule is required'), + check('txHash').isString().exists().withMessage('txHash is required'), + ], + showFirstValidationError, + getSynapseTxIdController +) + +export default router diff --git a/packages/rest-api/src/routes/index.ts b/packages/rest-api/src/routes/index.ts new file mode 100644 index 0000000000..b09c01f3b7 --- /dev/null +++ b/packages/rest-api/src/routes/index.ts @@ -0,0 +1,25 @@ +import express from 'express' + +import indexRoute from './indexRoute' +import swapRoute from './swapRoute' +import swapTxInfoRoute from './swapTxInfoRoute' +import bridgeRoute from './bridgeRoute' +import bridgeTxInfoRoute from './bridgeTxInfoRoute' +import getSynapseTxIdRoute from './getSynapseTxIdRoute' +import getBridgeTxStatusRoute from './getBridgeTxStatusRoute' +import getDestinationTxRoute from './getDestinationTxRoute' +import tokenListRoute from './tokenListRoute' + +const router = express.Router() + +router.use('/', indexRoute) +router.use('/swap', swapRoute) +router.use('/swapTxInfo', swapTxInfoRoute) +router.use('/bridge', bridgeRoute) +router.use('/bridgeTxInfo', bridgeTxInfoRoute) +router.use('/getSynapseTxId', getSynapseTxIdRoute) +router.use('/getBridgeTxStatus', getBridgeTxStatusRoute) +router.use('/getDestinationTx', getDestinationTxRoute) +router.use('/tokenList', tokenListRoute) + +export default router diff --git a/packages/rest-api/src/routes/indexRoute.ts b/packages/rest-api/src/routes/indexRoute.ts new file mode 100644 index 0000000000..e684732540 --- /dev/null +++ b/packages/rest-api/src/routes/indexRoute.ts @@ -0,0 +1,9 @@ +import express from 'express' + +import { indexController } from '../controllers/indexController' + +const router = express.Router() + +router.get('/', indexController) + +export default router diff --git a/packages/rest-api/src/routes/swapRoute.ts b/packages/rest-api/src/routes/swapRoute.ts new file mode 100644 index 0000000000..8aaeac316f --- /dev/null +++ b/packages/rest-api/src/routes/swapRoute.ts @@ -0,0 +1,28 @@ +import express from 'express' +import { check } from 'express-validator' + +import { validateTokens } from '../validations/validateTokens' +import { showFirstValidationError } from '../middleware/showFirstValidationError' +import { swapController } from '../controllers/swapController' +import { CHAINS_ARRAY } from '../constants/chains' + +const router = express.Router() + +router.get( + '/', + [ + check('chain') + .isNumeric() + .custom((value) => CHAINS_ARRAY.some((c) => c.id === Number(value))) + .withMessage('Unsupported chain') + .exists() + .withMessage('chain is required'), + validateTokens('chain', 'fromToken', 'fromToken'), + validateTokens('chain', 'toToken', 'toToken'), + check('amount').isNumeric().exists().withMessage('amount is required'), + ], + showFirstValidationError, + swapController +) + +export default router diff --git a/packages/rest-api/src/routes/swapTxInfoRoute.ts b/packages/rest-api/src/routes/swapTxInfoRoute.ts new file mode 100644 index 0000000000..9cc9805b6d --- /dev/null +++ b/packages/rest-api/src/routes/swapTxInfoRoute.ts @@ -0,0 +1,28 @@ +import express from 'express' +import { check } from 'express-validator' + +import { CHAINS_ARRAY } from '../constants/chains' +import { validateTokens } from '../validations/validateTokens' +import { showFirstValidationError } from '../middleware/showFirstValidationError' +import { swapTxInfoController } from '../controllers/swapTxInfoController' + +const router = express.Router() + +router.get( + '/', + [ + check('chain') + .isNumeric() + .custom((value) => CHAINS_ARRAY.some((c) => c.id === Number(value))) + .withMessage('Unsupported chain') + .exists() + .withMessage('chain is required'), + validateTokens('chain', 'fromToken', 'fromToken'), + validateTokens('chain', 'toToken', 'toToken'), + check('amount').isNumeric(), + ], + showFirstValidationError, + swapTxInfoController +) + +export default router diff --git a/packages/rest-api/src/routes/tokenListRoute.ts b/packages/rest-api/src/routes/tokenListRoute.ts new file mode 100644 index 0000000000..f4d2f83258 --- /dev/null +++ b/packages/rest-api/src/routes/tokenListRoute.ts @@ -0,0 +1,9 @@ +import express from 'express' + +import { tokenListController } from '../controllers/tokenListController' + +const router = express.Router() + +router.get('/', tokenListController) + +export default router diff --git a/packages/rest-api/src/services/synapseService.ts b/packages/rest-api/src/services/synapseService.ts new file mode 100644 index 0000000000..a76b9b729c --- /dev/null +++ b/packages/rest-api/src/services/synapseService.ts @@ -0,0 +1,11 @@ +import { JsonRpcProvider } from '@ethersproject/providers' +import { SynapseSDK } from '@synapsecns/sdk-router' + +import { CHAINS_ARRAY } from '../constants/chains' + +const providers = CHAINS_ARRAY.map( + (chain) => new JsonRpcProvider(chain.rpcUrls.primary) +) +const chainIds = CHAINS_ARRAY.map((chain) => chain.id) + +export const Synapse = new SynapseSDK(chainIds, providers) diff --git a/packages/rest-api/src/tests/bridgeRoute.test.ts b/packages/rest-api/src/tests/bridgeRoute.test.ts new file mode 100644 index 0000000000..16addfc80f --- /dev/null +++ b/packages/rest-api/src/tests/bridgeRoute.test.ts @@ -0,0 +1,75 @@ +import request from 'supertest' +import express from 'express' + +import bridgeRoute from '../routes/bridgeRoute' + +const app = express() +app.use('/bridge', bridgeRoute) + +describe('Bridge Route with Real Synapse Service', () => { + it('should return bridge quotes for valid input, 1000 USDC from Ethereum to Polygon', async () => { + const response = await request(app).get('/bridge').query({ + fromChain: '1', + toChain: '137', + fromToken: 'USDC', + toToken: 'USDC', + amount: '1000', + }) + expect(response.status).toBe(200) + expect(Array.isArray(response.body)).toBe(true) + expect(response.body.length).toBeGreaterThan(0) + expect(response.body[0]).toHaveProperty('maxAmountOutStr') + expect(response.body[0]).toHaveProperty('bridgeFeeFormatted') + }, 15000) + + it('should return 400 for unsupported fromChain, with error message', async () => { + const response = await request(app).get('/bridge').query({ + fromChain: '999', + toChain: '137', + fromToken: 'USDC', + toToken: 'USDC', + amount: '1000', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty( + 'message', + 'Unsupported fromChain' + ) + }, 10000) + + it('should return 400 for unsupported toChain, with error message', async () => { + const response = await request(app).get('/bridge').query({ + fromChain: '1', + toChain: '999', + fromToken: 'USDC', + toToken: 'USDC', + amount: '1000', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('message', 'Unsupported toChain') + }, 10000) + + it('should return 400 for missing fromToken, with error message', async () => { + const response = await request(app).get('/bridge').query({ + fromChain: '1', + toChain: '137', + toToken: 'USDC', + amount: '1000', + }) + + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'fromToken') + }, 10000) + + it('should return 400 for missing amount, with error message', async () => { + const response = await request(app).get('/bridge').query({ + fromChain: '1', + toChain: '137', + fromToken: 'USDC', + toToken: 'USDC', + }) + + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'amount') + }, 10000) +}) diff --git a/packages/rest-api/src/tests/bridgeTxInfoRoute.test.ts b/packages/rest-api/src/tests/bridgeTxInfoRoute.test.ts new file mode 100644 index 0000000000..8e83702336 --- /dev/null +++ b/packages/rest-api/src/tests/bridgeTxInfoRoute.test.ts @@ -0,0 +1,94 @@ +import request from 'supertest' +import express from 'express' + +import bridgeTxInfoRoute from '../routes/bridgeTxInfoRoute' + +const app = express() +app.use('/bridgeTxInfo', bridgeTxInfoRoute) + +describe('Bridge TX Info Route', () => { + it('should return bridge transaction info for valid input', async () => { + const response = await request(app).get('/bridgeTxInfo').query({ + fromChain: '1', + toChain: '137', + fromToken: 'USDC', + toToken: 'USDC', + amount: '1000', + destAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', + }) + + expect(response.status).toBe(200) + expect(Array.isArray(response.body)).toBe(true) + expect(response.body.length).toBeGreaterThan(0) + expect(response.body[0]).toHaveProperty('data') + expect(response.body[0]).toHaveProperty( + 'to', + '0xd5a597d6e7ddf373a92C8f477DAAA673b0902F48' + ) + }, 10_000) + + it('should return 400 for unsupported fromChain', async () => { + const response = await request(app).get('/bridgeTxInfo').query({ + fromChain: '999', + toChain: '137', + fromToken: 'USDC', + toToken: 'USDC', + amount: '1000', + destAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty( + 'message', + 'Unsupported fromChain' + ) + }, 10_000) + + it('should return 400 for unsupported toChain', async () => { + const response = await request(app).get('/bridgeTxInfo').query({ + fromChain: '1', + toChain: '999', + fromToken: 'USDC', + toToken: 'USDC', + amount: '1000', + destAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('message', 'Unsupported toChain') + }, 10_000) + + it('should return 400 for missing fromToken', async () => { + const response = await request(app).get('/bridgeTxInfo').query({ + fromChain: '1', + toChain: '137', + toToken: 'USDC', + amount: '1000', + destAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'fromToken') + }, 10_000) + + it('should return 400 for missing amount', async () => { + const response = await request(app).get('/bridgeTxInfo').query({ + fromChain: '1', + toChain: '137', + fromToken: 'USDC', + toToken: 'USDC', + destAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'amount') + }, 10_000) + + it('should return 400 for missing destAddress', async () => { + const response = await request(app).get('/bridgeTxInfo').query({ + fromChain: '1', + toChain: '137', + fromToken: 'USDC', + toToken: 'USDC', + amount: '1000', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'destAddress') + }, 10_000) +}) diff --git a/packages/rest-api/src/tests/getBridgeTxStatusRoute.test.ts b/packages/rest-api/src/tests/getBridgeTxStatusRoute.test.ts new file mode 100644 index 0000000000..f6a45ed4e7 --- /dev/null +++ b/packages/rest-api/src/tests/getBridgeTxStatusRoute.test.ts @@ -0,0 +1,86 @@ +import request from 'supertest' +import express from 'express' + +import getBridgeTxStatusRoute from '../routes/getBridgeTxStatusRoute' + +const app = express() +app.use('/getBridgeTxStatus', getBridgeTxStatusRoute) + +describe('Get Bridge TX Status Route', () => { + it('should return bridge transaction status for valid input', async () => { + const response = await request(app).get('/getBridgeTxStatus').query({ + destChainId: '42161', + bridgeModule: 'SynapseRFQ', + synapseTxId: + '0x9beb59b36ff4570d6b823b075dcd4fa9acd82dc4a28bf93a456ab8c93990604a', + }) + + expect(response.status).toBe(200) + expect(response.body).toHaveProperty('status') + expect(response.body).toHaveProperty('toInfo') + expect(response.body.status).toBe(true) + if (response.body.toInfo) { + expect(response.body.toInfo).toHaveProperty('chainID') + expect(response.body.toInfo).toHaveProperty('address') + expect(response.body.toInfo).toHaveProperty('txnHash') + expect(response.body.toInfo).toHaveProperty('formattedValue') + } + }, 10000) + + it('should return 400 for unsupported destChainId', async () => { + const response = await request(app).get('/getBridgeTxStatus').query({ + destChainId: '999', + bridgeModule: 'bridge', + synapseTxId: + '0x9beb59b36ff4570d6b823b075dcd4fa9acd82dc4a28bf93a456ab8c93990604a', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty( + 'message', + 'Unsupported destChainId' + ) + }, 10000) + + it('should return 400 for invalid bridgeModule', async () => { + const response = await request(app).get('/getBridgeTxStatus').query({ + destChainId: '1', + bridgeModule: 'invalidModule', + synapseTxId: + '0x9beb59b36ff4570d6b823b075dcd4fa9acd82dc4a28bf93a456ab8c93990604a', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty( + 'message', + 'Invalid bridge module. Must be one of: SynapseBridge, SynapseCCTP, SynapseRFQ' + ) + }, 10000) + + it('should return 400 for missing synapseTxId', async () => { + const response = await request(app).get('/getBridgeTxStatus').query({ + destChainId: '1', + bridgeModule: 'SynapseRFQ', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'synapseTxId') + }, 10000) + + it('should return 400 for missing destChainId', async () => { + const response = await request(app).get('/getBridgeTxStatus').query({ + bridgeModule: 'bridge', + synapseTxId: + '0x9beb59b36ff4570d6b823b075dcd4fa9acd82dc4a28bf93a456ab8c93990604a', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'destChainId') + }, 10000) + + it('should return 400 for missing bridgeModule', async () => { + const response = await request(app).get('/getBridgeTxStatus').query({ + destChainId: '137', + synapseTxId: + '0x9beb59b36ff4570d6b823b075dcd4fa9acd82dc4a28bf93a456ab8c93990604a', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'bridgeModule') + }, 10000) +}) diff --git a/packages/rest-api/src/tests/getDestinationTxRoute.test.ts b/packages/rest-api/src/tests/getDestinationTxRoute.test.ts new file mode 100644 index 0000000000..fca77246bc --- /dev/null +++ b/packages/rest-api/src/tests/getDestinationTxRoute.test.ts @@ -0,0 +1,58 @@ +import request from 'supertest' +import express from 'express' + +import getDestinationTxRoute from '../routes/getDestinationTxRoute' + +const app = express() +app.use('/getDestinationTx', getDestinationTxRoute) + +describe('Get Destination TX Route', () => { + it('should return destination transaction info for valid input', async () => { + const response = await request(app).get('/getDestinationTx').query({ + originChainId: '8453', + txHash: + '0x13486d9eaefd68de6a20b704d70deb8436effbac1f77fddfc0c7ef14f08e96c3', + }) + + expect(response.status).toBe(200) + expect(response.body).toHaveProperty('status') + expect(response.body).toHaveProperty('toInfo') + if (response.body.toInfo) { + expect(response.body.toInfo).toHaveProperty('chainID') + expect(response.body.toInfo).toHaveProperty('address') + expect(response.body.toInfo).toHaveProperty('txnHash') + expect(response.body.toInfo).toHaveProperty('formattedValue') + expect(response.body.toInfo).toHaveProperty('USDValue') + expect(response.body.toInfo).toHaveProperty('tokenSymbol') + expect(response.body.toInfo).toHaveProperty('blockNumber') + expect(response.body.toInfo).toHaveProperty('formattedTime') + } + }, 10000) + + it('should return 400 for missing originChainId', async () => { + const response = await request(app).get('/getDestinationTx').query({ + txHash: + '0x13486d9eaefd68de6a20b704d70deb8436effbac1f77fddfc0c7ef14f08e96c3', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'originChainId') + }, 10000) + + it('should return 400 for missing txHash', async () => { + const response = await request(app).get('/getDestinationTx').query({ + originChainId: '1', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'txHash') + }, 10000) + + it('should return 400 for non-numeric originChainId', async () => { + const response = await request(app).get('/getDestinationTx').query({ + originChainId: 'not-a-number', + txHash: + '0x13486d9eaefd68de6a20b704d70deb8436effbac1f77fddfc0c7ef14f08e96c3', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'originChainId') + }, 10000) +}) diff --git a/packages/rest-api/src/tests/getSynapseTxIdRoute.test.ts b/packages/rest-api/src/tests/getSynapseTxIdRoute.test.ts new file mode 100644 index 0000000000..b53e08528d --- /dev/null +++ b/packages/rest-api/src/tests/getSynapseTxIdRoute.test.ts @@ -0,0 +1,74 @@ +import request from 'supertest' +import express from 'express' + +import getSynapseTxIdRoute from '../routes/getSynapseTxIdRoute' + +const app = express() +app.use('/getSynapseTxId', getSynapseTxIdRoute) + +describe('Get Synapse TX ID Route', () => { + it('should return synapse transaction ID for valid input', async () => { + const response = await request(app).get('/getSynapseTxId').query({ + originChainId: '8453', + bridgeModule: 'SynapseRFQ', + txHash: + '0x13486d9eaefd68de6a20b704d70deb8436effbac1f77fddfc0c7ef14f08e96c3', + }) + expect(response.status).toBe(200) + expect(response.body).toHaveProperty('synapseTxId') + }, 10000) + + it('should return 400 for missing originChainId', async () => { + const response = await request(app).get('/getSynapseTxId').query({ + bridgeModule: 'SynapseRFQ', + txHash: + '0x13486d9eaefd68de6a20b704d70deb8436effbac1f77fddfc0c7ef14f08e96c3', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'originChainId') + }, 10000) + + it('should return 400 for missing bridgeModule', async () => { + const response = await request(app).get('/getSynapseTxId').query({ + originChainId: '1', + txHash: + '0x13486d9eaefd68de6a20b704d70deb8436effbac1f77fddfc0c7ef14f08e96c3', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'bridgeModule') + }, 10000) + + it('should return 400 for missing txHash', async () => { + const response = await request(app).get('/getSynapseTxId').query({ + originChainId: '1', + bridgeModule: 'SynapseRFQ', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'txHash') + }, 10000) + + it('should return 400 for non-numeric originChainId', async () => { + const response = await request(app).get('/getSynapseTxId').query({ + originChainId: 'not-a-number', + bridgeModule: 'SynapseRFQ', + txHash: + '0x13486d9eaefd68de6a20b704d70deb8436effbac1f77fddfc0c7ef14f08e96c3', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'originChainId') + }, 10000) + + it('should return 400 for invalid bridgeModule', async () => { + const response = await request(app).get('/getSynapseTxId').query({ + originChainId: '1', + bridgeModule: 'invalid_module', + txHash: + '0x13486d9eaefd68de6a20b704d70deb8436effbac1f77fddfc0c7ef14f08e96c3', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty( + 'message', + 'Invalid bridge module. Must be one of: SynapseBridge, SynapseCCTP, SynapseRFQ' + ) + }, 10000) +}) diff --git a/packages/rest-api/src/tests/indexRoute.test.ts b/packages/rest-api/src/tests/indexRoute.test.ts new file mode 100644 index 0000000000..3fd8e036af --- /dev/null +++ b/packages/rest-api/src/tests/indexRoute.test.ts @@ -0,0 +1,32 @@ +import request from 'supertest' +import express from 'express' + +import indexRoute from '../routes/indexRoute' + +const app = express() +app.use('/', indexRoute) + +describe('Index Route', () => { + it('should return welcome message', async () => { + const response = await request(app).get('/') + + expect(response.status).toBe(200) + + expect(response.body).toHaveProperty('message') + expect(response.body.message).toBe( + 'Welcome to the Synapse REST API for swap and bridge quotes' + ) + }) + + it('should return available chains and available tokens', async () => { + const response = await request(app).get('/') + + expect(response.status).toBe(200) + + expect(response.body).toHaveProperty('availableChains') + expect(response.body.availableChains.length).toBe(22) + + expect(response.body).toHaveProperty('availableTokens') + expect(response.body.availableTokens.length).toBe(62) + }) +}) diff --git a/packages/rest-api/src/tests/swapRoute.test.ts b/packages/rest-api/src/tests/swapRoute.test.ts new file mode 100644 index 0000000000..24d2e05861 --- /dev/null +++ b/packages/rest-api/src/tests/swapRoute.test.ts @@ -0,0 +1,58 @@ +import request from 'supertest' +import express from 'express' + +import swapRoute from '../routes/swapRoute' + +const app = express() +app.use('/swap', swapRoute) + +describe('Swap Route with Real Synapse Service', () => { + it('should return a real swap quote for valid input, 1000 USDC', async () => { + const response = await request(app).get('/swap').query({ + chain: '1', + fromToken: 'USDC', + toToken: 'DAI', + amount: '1000', + }) + + expect(response.status).toBe(200) + expect(response.body).toHaveProperty('maxAmountOut') + expect(response.body).toHaveProperty('routerAddress') + expect(response.body).toHaveProperty('query') + }, 10_000) + + it('should return 400 for unsupported chain, with error message', async () => { + const response = await request(app).get('/swap').query({ + chain: '111', + fromToken: 'USDC', + toToken: 'DAI', + amount: '1000', + }) + + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('message', 'Unsupported chain') + expect(response.body.error).toHaveProperty('field', 'chain') + }, 10_000) + + it('should return 400 for missing toToken, with error message', async () => { + const response = await request(app).get('/swap').query({ + chain: '1', + fromToken: 'USDC', + amount: '1000', + }) + + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'toToken') + }, 10_000) + + it('should return 400 for missing amount, with error message', async () => { + const response = await request(app).get('/swap').query({ + chain: '1', + fromToken: 'USDC', + toToken: 'DAI', + }) + + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'amount') + }, 10_000) +}) diff --git a/packages/rest-api/src/tests/swapTxInfoRoute.test.ts b/packages/rest-api/src/tests/swapTxInfoRoute.test.ts new file mode 100644 index 0000000000..3c682006da --- /dev/null +++ b/packages/rest-api/src/tests/swapTxInfoRoute.test.ts @@ -0,0 +1,54 @@ +import request from 'supertest' +import express from 'express' + +import swapTxInfoRoute from '../routes/swapTxInfoRoute' + +const app = express() +app.use('/swapTxInfo', swapTxInfoRoute) + +describe('Swap TX Info Route with Real Synapse Service', () => { + it('should return transaction info for valid input, 1000 USDC to DAI', async () => { + const response = await request(app).get('/swapTxInfo').query({ + chain: '1', + fromToken: 'USDC', + toToken: 'DAI', + amount: '1000', + }) + expect(response.status).toBe(200) + expect(response.body).toHaveProperty('data') + expect(response.body).toHaveProperty('to') + expect(response.body).toHaveProperty('value') + }, 10_000) + + it('should return 400 for unsupported chain, with error message', async () => { + const response = await request(app).get('/swapTxInfo').query({ + chain: '111', + fromToken: 'USDC', + toToken: 'DAI', + amount: '1000', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('message', 'Unsupported chain') + expect(response.body.error).toHaveProperty('field', 'chain') + }, 10_000) + + it('should return 400 for missing toToken, with error message', async () => { + const response = await request(app).get('/swapTxInfo').query({ + chain: '1', + fromToken: 'USDC', + amount: '1000', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'toToken') + }, 10_000) + + it('should return 400 for missing amount, with error message', async () => { + const response = await request(app).get('/swapTxInfo').query({ + chain: '1', + fromToken: 'USDC', + toToken: 'DAI', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'amount') + }, 10_000) +}) diff --git a/packages/rest-api/src/tests/tokenListRoute.test.ts b/packages/rest-api/src/tests/tokenListRoute.test.ts new file mode 100644 index 0000000000..16d2fb82df --- /dev/null +++ b/packages/rest-api/src/tests/tokenListRoute.test.ts @@ -0,0 +1,25 @@ +import request from 'supertest' +import express from 'express' + +import tokenListRoute from '../routes/tokenListRoute' + +const app = express() +app.use('/tokenList', tokenListRoute) + +describe('Index Route', () => { + it('should return a list of tokens with chain address mappings', async () => { + const response = await request(app).get('/tokenList') + + expect(response.status).toBe(200) + + const keys = Object.keys(response.body) + + expect(keys.length).toBe(62) + expect(response.body['ETH']['addresses']['1']).toBe( + '0x0000000000000000000000000000000000000000' + ) + expect(response.body['SYN']['addresses']['1']).toBe( + '0x0f2d719407fdbeff09d87557abb7232601fd9f29' + ) + }) +}) diff --git a/packages/rest-api/src/utils/findTokenInfo.ts b/packages/rest-api/src/utils/findTokenInfo.ts new file mode 100644 index 0000000000..7cd4e9d63d --- /dev/null +++ b/packages/rest-api/src/utils/findTokenInfo.ts @@ -0,0 +1,17 @@ +import { BRIDGE_MAP } from '../constants/bridgeMap' + +export const findTokenInfo = (chain: string, tokenSymbol: string) => { + const chainData = BRIDGE_MAP[chain] + if (!chainData) { + return null + } + for (const tokenAddress in chainData) { + if (chainData[tokenAddress].symbol === tokenSymbol) { + return { + address: tokenAddress, + decimals: chainData[tokenAddress].decimals, + } + } + } + return null +} diff --git a/packages/rest-api/src/utils/formatBNToString.ts b/packages/rest-api/src/utils/formatBNToString.ts new file mode 100644 index 0000000000..797edcbf80 --- /dev/null +++ b/packages/rest-api/src/utils/formatBNToString.ts @@ -0,0 +1,22 @@ +import { BigNumber } from '@ethersproject/bignumber' +import { formatUnits } from '@ethersproject/units' + +export const formatBNToString = ( + bn: BigNumber, + nativePrecision: number, + decimalPlaces = 18 +) => { + const fullPrecision = formatUnits(bn, nativePrecision) + const decimalIdx = fullPrecision.indexOf('.') + + if (decimalPlaces === undefined || decimalIdx === -1) { + return fullPrecision + } else { + const rawNumber = Number(fullPrecision) + + if (rawNumber === 0) { + return rawNumber.toFixed(1) + } + return rawNumber.toString() + } +} diff --git a/packages/rest-api/src/utils/getTokenDecimals.ts b/packages/rest-api/src/utils/getTokenDecimals.ts new file mode 100644 index 0000000000..5752188d50 --- /dev/null +++ b/packages/rest-api/src/utils/getTokenDecimals.ts @@ -0,0 +1,19 @@ +import * as tokensList from '../constants/bridgeable' + +export const getTokenDecimals = ( + chainId: number, + tokenAddress: string +): number => { + for (const [, token] of Object.entries(tokensList)) { + if ( + token.addresses[chainId]?.toLowerCase() === tokenAddress.toLowerCase() + ) { + const decimals = + typeof token.decimals === 'object' + ? token.decimals[chainId] + : token.decimals + return decimals + } + } + return 18 +} diff --git a/packages/rest-api/src/validations/validateTokens.ts b/packages/rest-api/src/validations/validateTokens.ts new file mode 100644 index 0000000000..c787115e1e --- /dev/null +++ b/packages/rest-api/src/validations/validateTokens.ts @@ -0,0 +1,22 @@ +import { check } from 'express-validator' + +import { findTokenInfo } from '../utils/findTokenInfo' + +export const validateTokens = (chainParam, tokenParam, paramName) => { + return check(tokenParam) + .isString() + .exists() + .withMessage(`${paramName} is required`) + .custom((value, { req }) => { + const chain = req.query[chainParam] + const tokenInfo = findTokenInfo(chain, value) + if (!tokenInfo) { + throw new Error(`Invalid ${paramName} symbol`) + } + if (!req.res.locals.tokenInfo) { + req.res.locals.tokenInfo = {} + } + req.res.locals.tokenInfo[paramName] = tokenInfo + return true + }) +} diff --git a/packages/rest-api/tsconfig.json b/packages/rest-api/tsconfig.json index 7fcbd74c35..a16e656bf2 100644 --- a/packages/rest-api/tsconfig.json +++ b/packages/rest-api/tsconfig.json @@ -1,16 +1,9 @@ { - "include": [ - "./src/**/*", - "./src/config/**/*" - ], + "include": ["./src/**/*", "./src/config/**/*"], "compilerOptions": { "target": "esnext", "module": "CommonJS", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "outDir": "./dist", // Transpiled files will be output to the 'dist' folder "importHelpers": true, "declaration": true, @@ -20,7 +13,7 @@ // linter checks for common issues "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, - "noUnusedLocals": true, + "noUnusedLocals": false, "noUnusedParameters": true, "moduleResolution": "node", "jsx": "react", @@ -29,9 +22,7 @@ "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "allowSyntheticDefaultImports": true, - "downlevelIteration": true, + "downlevelIteration": true }, - "files": [ - "src/app.ts" - ] + "files": ["src/app.ts"] } diff --git a/packages/synapse-interface/CHANGELOG.md b/packages/synapse-interface/CHANGELOG.md index d89d22a7a5..6e75c970f7 100644 --- a/packages/synapse-interface/CHANGELOG.md +++ b/packages/synapse-interface/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.38.3](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.38.2...@synapsecns/synapse-interface@0.38.3) (2024-09-10) + +**Note:** Version bump only for package @synapsecns/synapse-interface + + + + + ## [0.38.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.38.1...@synapsecns/synapse-interface@0.38.2) (2024-09-10) **Note:** Version bump only for package @synapsecns/synapse-interface diff --git a/packages/synapse-interface/components/LanguageSelector.tsx b/packages/synapse-interface/components/LanguageSelector.tsx index 2b51d5f920..c9236ee246 100644 --- a/packages/synapse-interface/components/LanguageSelector.tsx +++ b/packages/synapse-interface/components/LanguageSelector.tsx @@ -12,6 +12,7 @@ const languages = [ { code: 'es', name: 'Español' }, { code: 'fr', name: 'Français' }, { code: 'tr', name: 'Türkçe' }, + { code: 'zh-CN', name: '中文(简体)' }, ] export const LanguageSelector = () => { diff --git a/packages/synapse-interface/next.config.js b/packages/synapse-interface/next.config.js index 5e59674626..2b7541ac02 100644 --- a/packages/synapse-interface/next.config.js +++ b/packages/synapse-interface/next.config.js @@ -44,7 +44,7 @@ const nextConfig = { tsconfigPath: './tsconfig.json', }, i18n: { - locales: ['en-US', 'fr', 'ar', 'tr', 'es'], + locales: ['en-US', 'fr', 'ar', 'tr', 'es', 'zh-CN'], defaultLocale: 'en-US', }, } diff --git a/packages/synapse-interface/package.json b/packages/synapse-interface/package.json index 006659796b..3c6c2abaf4 100644 --- a/packages/synapse-interface/package.json +++ b/packages/synapse-interface/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/synapse-interface", - "version": "0.38.2", + "version": "0.38.3", "private": true, "engines": { "node": ">=18.18.0" diff --git a/packages/synapse-interface/public/blacklist.json b/packages/synapse-interface/public/blacklist.json index 6d854278c5..99fd9dba86 100644 --- a/packages/synapse-interface/public/blacklist.json +++ b/packages/synapse-interface/public/blacklist.json @@ -534,5 +534,6 @@ "0x8e80e055e2790c9dFEeCa5D9ce11a485e70eB244", "0xfdc0dfbcbe0afad28827dfa216b9adb9cdb90dfd", "0xE484E76932fF72dD3B0867B9dC4C0690E081C2F5", - "0x1Da02f048F0b4Fc57169f1958f996b317518029a" + "0x1Da02f048F0b4Fc57169f1958f996b317518029a", + "0xB0A2e43D3E0dc4C71346A71484aC6a2627bbCbeD" ] diff --git a/services/cctp-relayer/contracts/cctp/cctp.metadata.go b/services/cctp-relayer/contracts/cctp/cctp.metadata.go index e022b5a36f..666d060980 100644 --- a/services/cctp-relayer/contracts/cctp/cctp.metadata.go +++ b/services/cctp-relayer/contracts/cctp/cctp.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed cctp.contractinfo.json var rawContracts []byte diff --git a/services/cctp-relayer/contracts/messagetransmitter/messagetransmitter.metadata.go b/services/cctp-relayer/contracts/messagetransmitter/messagetransmitter.metadata.go index e54938acc1..eec61db4b0 100644 --- a/services/cctp-relayer/contracts/messagetransmitter/messagetransmitter.metadata.go +++ b/services/cctp-relayer/contracts/messagetransmitter/messagetransmitter.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed messagetransmitter.contractinfo.json var rawContracts []byte diff --git a/services/cctp-relayer/contracts/mockmessagetransmitter/mockmessagetransmitter.metadata.go b/services/cctp-relayer/contracts/mockmessagetransmitter/mockmessagetransmitter.metadata.go index 4d77872f46..092bbec44a 100644 --- a/services/cctp-relayer/contracts/mockmessagetransmitter/mockmessagetransmitter.metadata.go +++ b/services/cctp-relayer/contracts/mockmessagetransmitter/mockmessagetransmitter.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed mockmessagetransmitter.contractinfo.json var rawContracts []byte diff --git a/services/cctp-relayer/contracts/mockmintburntoken/mockmintburntoken.metadata.go b/services/cctp-relayer/contracts/mockmintburntoken/mockmintburntoken.metadata.go index b1bb6f0bb1..2432a38758 100644 --- a/services/cctp-relayer/contracts/mockmintburntoken/mockmintburntoken.metadata.go +++ b/services/cctp-relayer/contracts/mockmintburntoken/mockmintburntoken.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed mockmintburntoken.contractinfo.json var rawContracts []byte diff --git a/services/cctp-relayer/contracts/mocktokenmessenger/mocktokenmessenger.metadata.go b/services/cctp-relayer/contracts/mocktokenmessenger/mocktokenmessenger.metadata.go index 0c0a4144d5..e0d1606591 100644 --- a/services/cctp-relayer/contracts/mocktokenmessenger/mocktokenmessenger.metadata.go +++ b/services/cctp-relayer/contracts/mocktokenmessenger/mocktokenmessenger.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed mocktokenmessenger.contractinfo.json var rawContracts []byte diff --git a/services/cctp-relayer/contracts/mocktokenminter/mocktokenminter.metadata.go b/services/cctp-relayer/contracts/mocktokenminter/mocktokenminter.metadata.go index 48cbf836e1..4bc2437054 100644 --- a/services/cctp-relayer/contracts/mocktokenminter/mocktokenminter.metadata.go +++ b/services/cctp-relayer/contracts/mocktokenminter/mocktokenminter.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed mocktokenminter.contractinfo.json var rawContracts []byte diff --git a/services/cctp-relayer/contracts/tokenmessenger/tokenmessenger.metadata.go b/services/cctp-relayer/contracts/tokenmessenger/tokenmessenger.metadata.go index f67105a1a8..9ea8e42047 100644 --- a/services/cctp-relayer/contracts/tokenmessenger/tokenmessenger.metadata.go +++ b/services/cctp-relayer/contracts/tokenmessenger/tokenmessenger.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed tokenmessenger.contractinfo.json var rawContracts []byte diff --git a/services/explorer/contracts/bridge/bridge.metadata.go b/services/explorer/contracts/bridge/bridge.metadata.go index 7ceb294bdf..42e5b47fea 100644 --- a/services/explorer/contracts/bridge/bridge.metadata.go +++ b/services/explorer/contracts/bridge/bridge.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed bridge.contractinfo.json var rawContracts []byte diff --git a/services/explorer/contracts/bridge/bridgev1/bridgev1.metadata.go b/services/explorer/contracts/bridge/bridgev1/bridgev1.metadata.go index 1f1326671a..130579c8fa 100644 --- a/services/explorer/contracts/bridge/bridgev1/bridgev1.metadata.go +++ b/services/explorer/contracts/bridge/bridgev1/bridgev1.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed bridgev1.contractinfo.json var rawContracts []byte diff --git a/services/explorer/contracts/bridge/testbridge/testbridge.metadata.go b/services/explorer/contracts/bridge/testbridge/testbridge.metadata.go index d012e57c9b..8a22cf91f5 100644 --- a/services/explorer/contracts/bridge/testbridge/testbridge.metadata.go +++ b/services/explorer/contracts/bridge/testbridge/testbridge.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed testbridge.contractinfo.json var rawContracts []byte diff --git a/services/explorer/contracts/bridge/testbridgev1/testbridgev1.metadata.go b/services/explorer/contracts/bridge/testbridgev1/testbridgev1.metadata.go index a824a5ef3e..09b3a60764 100644 --- a/services/explorer/contracts/bridge/testbridgev1/testbridgev1.metadata.go +++ b/services/explorer/contracts/bridge/testbridgev1/testbridgev1.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed testbridgev1.contractinfo.json var rawContracts []byte diff --git a/services/explorer/contracts/bridgeconfig/bridgeconfig.metadata.go b/services/explorer/contracts/bridgeconfig/bridgeconfig.metadata.go index f4428b42db..20bc66e250 100644 --- a/services/explorer/contracts/bridgeconfig/bridgeconfig.metadata.go +++ b/services/explorer/contracts/bridgeconfig/bridgeconfig.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed bridgeconfig.contractinfo.json var rawContracts []byte diff --git a/services/explorer/contracts/cctp/cctp.metadata.go b/services/explorer/contracts/cctp/cctp.metadata.go index e022b5a36f..666d060980 100644 --- a/services/explorer/contracts/cctp/cctp.metadata.go +++ b/services/explorer/contracts/cctp/cctp.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed cctp.contractinfo.json var rawContracts []byte diff --git a/services/explorer/contracts/cctp/testcctp/testcctp.metadata.go b/services/explorer/contracts/cctp/testcctp/testcctp.metadata.go index 41fc389329..b9d4783013 100644 --- a/services/explorer/contracts/cctp/testcctp/testcctp.metadata.go +++ b/services/explorer/contracts/cctp/testcctp/testcctp.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed testcctp.contractinfo.json var rawContracts []byte diff --git a/services/explorer/contracts/fastbridge/fastbridge.metadata.go b/services/explorer/contracts/fastbridge/fastbridge.metadata.go index 3f65f849f2..3f1f36e8ab 100644 --- a/services/explorer/contracts/fastbridge/fastbridge.metadata.go +++ b/services/explorer/contracts/fastbridge/fastbridge.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed fastbridge.contractinfo.json var rawContracts []byte diff --git a/services/explorer/contracts/fastbridge/testfastbridge/testfastbridge.metadata.go b/services/explorer/contracts/fastbridge/testfastbridge/testfastbridge.metadata.go index b9c4944ce8..7aa9e787a5 100644 --- a/services/explorer/contracts/fastbridge/testfastbridge/testfastbridge.metadata.go +++ b/services/explorer/contracts/fastbridge/testfastbridge/testfastbridge.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed testfastbridge.contractinfo.json var rawContracts []byte diff --git a/services/explorer/contracts/messagebus/messagebus.metadata.go b/services/explorer/contracts/messagebus/messagebus.metadata.go index 6814c0e038..c4fc2a82c8 100644 --- a/services/explorer/contracts/messagebus/messagebus.metadata.go +++ b/services/explorer/contracts/messagebus/messagebus.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed messagebus.contractinfo.json var rawContracts []byte diff --git a/services/explorer/contracts/messagebus/testmessagebus/testmessagebus.metadata.go b/services/explorer/contracts/messagebus/testmessagebus/testmessagebus.metadata.go index c2eca70427..832ae1021e 100644 --- a/services/explorer/contracts/messagebus/testmessagebus/testmessagebus.metadata.go +++ b/services/explorer/contracts/messagebus/testmessagebus/testmessagebus.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed testmessagebus.contractinfo.json var rawContracts []byte diff --git a/services/explorer/contracts/metaswap/metaswap.metadata.go b/services/explorer/contracts/metaswap/metaswap.metadata.go index d5c64b2ba3..75bdaae546 100644 --- a/services/explorer/contracts/metaswap/metaswap.metadata.go +++ b/services/explorer/contracts/metaswap/metaswap.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed metaswap.contractinfo.json var rawContracts []byte diff --git a/services/explorer/contracts/metaswap/testmetaswap/testmetaswap.metadata.go b/services/explorer/contracts/metaswap/testmetaswap/testmetaswap.metadata.go index 0331f00318..f4d6cf9f91 100644 --- a/services/explorer/contracts/metaswap/testmetaswap/testmetaswap.metadata.go +++ b/services/explorer/contracts/metaswap/testmetaswap/testmetaswap.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed testmetaswap.contractinfo.json var rawContracts []byte diff --git a/services/explorer/contracts/swap/swap.metadata.go b/services/explorer/contracts/swap/swap.metadata.go index 272b4d9029..b86f649180 100644 --- a/services/explorer/contracts/swap/swap.metadata.go +++ b/services/explorer/contracts/swap/swap.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed swap.contractinfo.json var rawContracts []byte diff --git a/services/explorer/contracts/swap/testswap/testswap.metadata.go b/services/explorer/contracts/swap/testswap/testswap.metadata.go index 74eba6575a..3fb52e5728 100644 --- a/services/explorer/contracts/swap/testswap/testswap.metadata.go +++ b/services/explorer/contracts/swap/testswap/testswap.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed testswap.contractinfo.json var rawContracts []byte diff --git a/services/explorer/contracts/user/dfk/dfkhero/dfkhero.metadata.go b/services/explorer/contracts/user/dfk/dfkhero/dfkhero.metadata.go index 3df81dc65c..930cf0c123 100644 --- a/services/explorer/contracts/user/dfk/dfkhero/dfkhero.metadata.go +++ b/services/explorer/contracts/user/dfk/dfkhero/dfkhero.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed dfkhero.contractinfo.json var rawContracts []byte diff --git a/services/explorer/contracts/user/dfk/dfkpet/dfkpet.metadata.go b/services/explorer/contracts/user/dfk/dfkpet/dfkpet.metadata.go index 7b548370d8..2cf3102c95 100644 --- a/services/explorer/contracts/user/dfk/dfkpet/dfkpet.metadata.go +++ b/services/explorer/contracts/user/dfk/dfkpet/dfkpet.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed dfkpet.contractinfo.json var rawContracts []byte diff --git a/services/explorer/contracts/user/dfk/dfktear/dfktear.metadata.go b/services/explorer/contracts/user/dfk/dfktear/dfktear.metadata.go index 4b3f756f87..05506e642f 100644 --- a/services/explorer/contracts/user/dfk/dfktear/dfktear.metadata.go +++ b/services/explorer/contracts/user/dfk/dfktear/dfktear.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed dfktear.contractinfo.json var rawContracts []byte diff --git a/services/rfq/api/docs/docs.go b/services/rfq/api/docs/docs.go index c68a9eb52c..af06bfa449 100644 --- a/services/rfq/api/docs/docs.go +++ b/services/rfq/api/docs/docs.go @@ -41,7 +41,13 @@ const docTemplate = `{ ], "responses": { "200": { - "description": "OK" + "description": "OK", + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } + } } } } @@ -72,7 +78,13 @@ const docTemplate = `{ ], "responses": { "200": { - "description": "OK" + "description": "OK", + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } + } } } } @@ -98,6 +110,12 @@ const docTemplate = `{ "items": { "$ref": "#/definitions/model.GetContractsResponse" } + }, + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } } } } @@ -156,6 +174,12 @@ const docTemplate = `{ "items": { "$ref": "#/definitions/model.GetQuoteResponse" } + }, + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } } } } @@ -185,7 +209,13 @@ const docTemplate = `{ ], "responses": { "200": { - "description": "OK" + "description": "OK", + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } + } } } } diff --git a/services/rfq/api/docs/swagger.json b/services/rfq/api/docs/swagger.json index f6bdd6e25b..5d28bbf785 100644 --- a/services/rfq/api/docs/swagger.json +++ b/services/rfq/api/docs/swagger.json @@ -30,7 +30,13 @@ ], "responses": { "200": { - "description": "OK" + "description": "OK", + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } + } } } } @@ -61,7 +67,13 @@ ], "responses": { "200": { - "description": "OK" + "description": "OK", + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } + } } } } @@ -87,6 +99,12 @@ "items": { "$ref": "#/definitions/model.GetContractsResponse" } + }, + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } } } } @@ -145,6 +163,12 @@ "items": { "$ref": "#/definitions/model.GetQuoteResponse" } + }, + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } } } } @@ -174,7 +198,13 @@ ], "responses": { "200": { - "description": "OK" + "description": "OK", + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } + } } } } diff --git a/services/rfq/api/docs/swagger.yaml b/services/rfq/api/docs/swagger.yaml index c39cace1ef..a8ddffdcc6 100644 --- a/services/rfq/api/docs/swagger.yaml +++ b/services/rfq/api/docs/swagger.yaml @@ -99,6 +99,10 @@ paths: responses: "200": description: OK + headers: + X-Api-Version: + description: API Version Number - See docs for more info + type: string summary: Relay ack tags: - ack @@ -119,6 +123,10 @@ paths: responses: "200": description: OK + headers: + X-Api-Version: + description: API Version Number - See docs for more info + type: string summary: Upsert quotes tags: - quotes @@ -132,6 +140,10 @@ paths: responses: "200": description: OK + headers: + X-Api-Version: + description: API Version Number - See docs for more info + type: string schema: items: $ref: '#/definitions/model.GetContractsResponse' @@ -170,6 +182,10 @@ paths: responses: "200": description: OK + headers: + X-Api-Version: + description: API Version Number - See docs for more info + type: string schema: items: $ref: '#/definitions/model.GetQuoteResponse' @@ -193,6 +209,10 @@ paths: responses: "200": description: OK + headers: + X-Api-Version: + description: API Version Number - See docs for more info + type: string summary: Upsert quote tags: - quotes diff --git a/services/rfq/api/rest/handler.go b/services/rfq/api/rest/handler.go index 4e2f61b802..2878fb4cb3 100644 --- a/services/rfq/api/rest/handler.go +++ b/services/rfq/api/rest/handler.go @@ -49,6 +49,7 @@ func APIVersionMiddleware(serverVersion string) gin.HandlerFunc { // @Accept json // @Produce json // @Success 200 +// @Header 200 {string} X-Api-Version "API Version Number - See docs for more info" // @Router /quotes [put]. func (h *Handler) ModifyQuote(c *gin.Context) { // Retrieve the request from context @@ -94,6 +95,7 @@ func (h *Handler) ModifyQuote(c *gin.Context) { // @Accept json // @Produce json // @Success 200 +// @Header 200 {string} X-Api-Version "API Version Number - See docs for more info" // @Router /bulk_quotes [put]. func (h *Handler) ModifyBulkQuotes(c *gin.Context) { // Retrieve the request from context @@ -176,6 +178,7 @@ func parseDBQuote(putRequest model.PutQuoteRequest, relayerAddr interface{}) (*d // @Accept json // @Produce json // @Success 200 {array} model.GetQuoteResponse +// @Header 200 {string} X-Api-Version "API Version Number - See docs for more info" // @Router /quotes [get]. func (h *Handler) GetQuotes(c *gin.Context) { originChainIDStr := c.Query("originChainID") @@ -240,6 +243,7 @@ func (h *Handler) GetQuotes(c *gin.Context) { // @Accept json // @Produce json // @Success 200 {array} model.GetContractsResponse +// @Header 200 {string} X-Api-Version "API Version Number - See docs for more info" // @Router /contracts [get]. func (h *Handler) GetContracts(c *gin.Context) { // Convert quotes from db model to api model diff --git a/services/rfq/api/rest/server.go b/services/rfq/api/rest/server.go index 3c961b65f3..a3a3b32a6f 100644 --- a/services/rfq/api/rest/server.go +++ b/services/rfq/api/rest/server.go @@ -330,6 +330,7 @@ func (r *QuoterAPIServer) checkRole(c *gin.Context, destChainID uint32) (address // @Accept json // @Produce json // @Success 200 +// @Header 200 {string} X-Api-Version "API Version Number - See docs for more info" // @Router /ack [put]. func (r *QuoterAPIServer) PutRelayAck(c *gin.Context) { req, exists := c.Get("putRequest") diff --git a/services/rfq/contracts/fastbridge/fastbridge.metadata.go b/services/rfq/contracts/fastbridge/fastbridge.metadata.go index 3f65f849f2..3f1f36e8ab 100644 --- a/services/rfq/contracts/fastbridge/fastbridge.metadata.go +++ b/services/rfq/contracts/fastbridge/fastbridge.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed fastbridge.contractinfo.json var rawContracts []byte diff --git a/services/rfq/contracts/ierc20/ierc20.metadata.go b/services/rfq/contracts/ierc20/ierc20.metadata.go index 658ce7f486..3e87624e2b 100644 --- a/services/rfq/contracts/ierc20/ierc20.metadata.go +++ b/services/rfq/contracts/ierc20/ierc20.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed ierc20.contractinfo.json var rawContracts []byte diff --git a/services/rfq/contracts/l1gateway/l1gateway.metadata.go b/services/rfq/contracts/l1gateway/l1gateway.metadata.go index f375495065..dcfe031c9d 100644 --- a/services/rfq/contracts/l1gateway/l1gateway.metadata.go +++ b/services/rfq/contracts/l1gateway/l1gateway.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed l1gateway.contractinfo.json var rawContracts []byte diff --git a/services/rfq/contracts/l1scrollmessenger/l1scrollmessenger.metadata.go b/services/rfq/contracts/l1scrollmessenger/l1scrollmessenger.metadata.go index 9d92a97c2a..898c74cf7e 100644 --- a/services/rfq/contracts/l1scrollmessenger/l1scrollmessenger.metadata.go +++ b/services/rfq/contracts/l1scrollmessenger/l1scrollmessenger.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed l1scrollmessenger.contractinfo.json var rawContracts []byte diff --git a/services/rfq/contracts/l2gateway/l2gateway.metadata.go b/services/rfq/contracts/l2gateway/l2gateway.metadata.go index aa05522936..088ef902ef 100644 --- a/services/rfq/contracts/l2gateway/l2gateway.metadata.go +++ b/services/rfq/contracts/l2gateway/l2gateway.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed l2gateway.contractinfo.json var rawContracts []byte diff --git a/services/rfq/contracts/testcontracts/dai/dai.metadata.go b/services/rfq/contracts/testcontracts/dai/dai.metadata.go index 6b51ed83b7..80d53b6779 100644 --- a/services/rfq/contracts/testcontracts/dai/dai.metadata.go +++ b/services/rfq/contracts/testcontracts/dai/dai.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed dai.contractinfo.json var rawContracts []byte diff --git a/services/rfq/contracts/testcontracts/fastbridgemock/fastbridgemock.metadata.go b/services/rfq/contracts/testcontracts/fastbridgemock/fastbridgemock.metadata.go index e3c5cd8ec6..3c8d7f64aa 100644 --- a/services/rfq/contracts/testcontracts/fastbridgemock/fastbridgemock.metadata.go +++ b/services/rfq/contracts/testcontracts/fastbridgemock/fastbridgemock.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed fastbridgemock.contractinfo.json var rawContracts []byte diff --git a/services/rfq/contracts/testcontracts/mockerc20/mockerc20.metadata.go b/services/rfq/contracts/testcontracts/mockerc20/mockerc20.metadata.go index 623fce1f73..39152f95a1 100644 --- a/services/rfq/contracts/testcontracts/mockerc20/mockerc20.metadata.go +++ b/services/rfq/contracts/testcontracts/mockerc20/mockerc20.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed mockerc20.contractinfo.json var rawContracts []byte diff --git a/services/rfq/contracts/testcontracts/usdc/FiatToken.metadata.go b/services/rfq/contracts/testcontracts/usdc/FiatToken.metadata.go index 4dd900db6d..94e5531ed4 100644 --- a/services/rfq/contracts/testcontracts/usdc/FiatToken.metadata.go +++ b/services/rfq/contracts/testcontracts/usdc/FiatToken.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed FiatToken.contractinfo.json var rawContracts []byte diff --git a/services/rfq/contracts/testcontracts/weth9/weth9.metadata.go b/services/rfq/contracts/testcontracts/weth9/weth9.metadata.go index dc6f7481b0..de57db1878 100644 --- a/services/rfq/contracts/testcontracts/weth9/weth9.metadata.go +++ b/services/rfq/contracts/testcontracts/weth9/weth9.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed weth9.contractinfo.json var rawContracts []byte diff --git a/services/scribe/testutil/testcontract/testcontract.metadata.go b/services/scribe/testutil/testcontract/testcontract.metadata.go index ccddc30a91..3a125002d6 100644 --- a/services/scribe/testutil/testcontract/testcontract.metadata.go +++ b/services/scribe/testutil/testcontract/testcontract.metadata.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed testcontract.contractinfo.json var rawContracts []byte diff --git a/tools/abigen/internal/generate.go b/tools/abigen/internal/generate.go index 5c5141e7d8..1ce183c2b3 100644 --- a/tools/abigen/internal/generate.go +++ b/tools/abigen/internal/generate.go @@ -235,7 +235,7 @@ import ( "github.com/ethereum/go-ethereum/common/compiler" ) -// rawContracts are the json we use to dervive the processed contracts +// rawContracts are the json we use to derive the processed contracts // //go:embed {{ .Name }}.contractinfo.json var rawContracts []byte diff --git a/yarn.lock b/yarn.lock index 37f429bc81..8218540784 100644 --- a/yarn.lock +++ b/yarn.lock @@ -278,6 +278,11 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.9.tgz#53eee4e68f1c1d0282aa0eb05ddb02d033fc43a0" integrity sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng== +"@babel/compat-data@^7.25.2", "@babel/compat-data@^7.25.4": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.25.4.tgz#7d2a80ce229890edcf4cc259d4d696cb4dae2fcb" + integrity sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ== + "@babel/core@7.12.9": version "7.12.9" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.9.tgz#fd450c4ec10cdbb980e2928b7aa7a28484593fc8" @@ -363,6 +368,27 @@ json5 "^2.2.3" semver "^6.3.1" +"@babel/core@^7.25.2": + version "7.25.2" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.25.2.tgz#ed8eec275118d7613e77a352894cd12ded8eba77" + integrity sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.24.7" + "@babel/generator" "^7.25.0" + "@babel/helper-compilation-targets" "^7.25.2" + "@babel/helper-module-transforms" "^7.25.2" + "@babel/helpers" "^7.25.0" + "@babel/parser" "^7.25.0" + "@babel/template" "^7.25.0" + "@babel/traverse" "^7.25.2" + "@babel/types" "^7.25.2" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + "@babel/eslint-parser@^7.18.2": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.24.1.tgz#e27eee93ed1d271637165ef3a86e2b9332395c32" @@ -422,6 +448,16 @@ "@jridgewell/trace-mapping" "^0.3.25" jsesc "^2.5.1" +"@babel/generator@^7.25.0", "@babel/generator@^7.25.6": + version "7.25.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.25.6.tgz#0df1ad8cb32fe4d2b01d8bf437f153d19342a87c" + integrity sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw== + dependencies: + "@babel/types" "^7.25.6" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.18.6", "@babel/helper-annotate-as-pure@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" @@ -429,6 +465,13 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-annotate-as-pure@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz#5373c7bc8366b12a033b4be1ac13a206c6656aab" + integrity sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg== + dependencies: + "@babel/types" "^7.24.7" + "@babel/helper-builder-binary-assignment-operator-visitor@^7.22.15": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz#5426b109cf3ad47b91120f8328d8ab1be8b0b956" @@ -436,6 +479,14 @@ dependencies: "@babel/types" "^7.22.15" +"@babel/helper-builder-binary-assignment-operator-visitor@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz#37d66feb012024f2422b762b9b2a7cfe27c7fba3" + integrity sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA== + dependencies: + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" + "@babel/helper-compilation-targets@^7.10.4", "@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.23.6": version "7.23.6" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" @@ -447,6 +498,17 @@ lru-cache "^5.1.1" semver "^6.3.1" +"@babel/helper-compilation-targets@^7.24.7", "@babel/helper-compilation-targets@^7.25.2": + version "7.25.2" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz#e1d9410a90974a3a5a66e84ff55ef62e3c02d06c" + integrity sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw== + dependencies: + "@babel/compat-data" "^7.25.2" + "@babel/helper-validator-option" "^7.24.8" + browserslist "^4.23.1" + lru-cache "^5.1.1" + semver "^6.3.1" + "@babel/helper-compilation-targets@^7.24.8": version "7.24.8" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.8.tgz#b607c3161cd9d1744977d4f97139572fe778c271" @@ -488,6 +550,19 @@ "@babel/helper-split-export-declaration" "^7.24.5" semver "^6.3.1" +"@babel/helper-create-class-features-plugin@^7.24.7", "@babel/helper-create-class-features-plugin@^7.25.0", "@babel/helper-create-class-features-plugin@^7.25.4": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.4.tgz#57eaf1af38be4224a9d9dd01ddde05b741f50e14" + integrity sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.24.7" + "@babel/helper-member-expression-to-functions" "^7.24.8" + "@babel/helper-optimise-call-expression" "^7.24.7" + "@babel/helper-replace-supers" "^7.25.0" + "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" + "@babel/traverse" "^7.25.4" + semver "^6.3.1" + "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.15", "@babel/helper-create-regexp-features-plugin@^7.22.5": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz#5ee90093914ea09639b01c711db0d6775e558be1" @@ -497,6 +572,15 @@ regexpu-core "^5.3.1" semver "^6.3.1" +"@babel/helper-create-regexp-features-plugin@^7.24.7", "@babel/helper-create-regexp-features-plugin@^7.25.0", "@babel/helper-create-regexp-features-plugin@^7.25.2": + version "7.25.2" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz#24c75974ed74183797ffd5f134169316cd1808d9" + integrity sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.24.7" + regexpu-core "^5.3.1" + semver "^6.3.1" + "@babel/helper-define-polyfill-provider@^0.0.3": version "0.0.3" resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.0.3.tgz#df9da66285b884ce66417abdd0b6ca91198149bd" @@ -536,6 +620,17 @@ lodash.debounce "^4.0.8" resolve "^1.14.2" +"@babel/helper-define-polyfill-provider@^0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz#18594f789c3594acb24cfdb4a7f7b7d2e8bd912d" + integrity sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ== + dependencies: + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-plugin-utils" "^7.22.5" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + "@babel/helper-environment-visitor@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" @@ -592,6 +687,14 @@ dependencies: "@babel/types" "^7.24.5" +"@babel/helper-member-expression-to-functions@^7.24.8": + version "7.24.8" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz#6155e079c913357d24a4c20480db7c712a5c3fb6" + integrity sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA== + dependencies: + "@babel/traverse" "^7.24.8" + "@babel/types" "^7.24.8" + "@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.22.15", "@babel/helper-module-imports@^7.24.1", "@babel/helper-module-imports@^7.24.3": version "7.24.3" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz#6ac476e6d168c7c23ff3ba3cf4f7841d46ac8128" @@ -629,6 +732,16 @@ "@babel/helper-split-export-declaration" "^7.24.5" "@babel/helper-validator-identifier" "^7.24.5" +"@babel/helper-module-transforms@^7.24.7", "@babel/helper-module-transforms@^7.25.0", "@babel/helper-module-transforms@^7.25.2": + version "7.25.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz#ee713c29768100f2776edf04d4eb23b8d27a66e6" + integrity sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ== + dependencies: + "@babel/helper-module-imports" "^7.24.7" + "@babel/helper-simple-access" "^7.24.7" + "@babel/helper-validator-identifier" "^7.24.7" + "@babel/traverse" "^7.25.2" + "@babel/helper-module-transforms@^7.24.8", "@babel/helper-module-transforms@^7.24.9": version "7.24.9" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.24.9.tgz#e13d26306b89eea569180868e652e7f514de9d29" @@ -647,6 +760,13 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-optimise-call-expression@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz#8b0a0456c92f6b323d27cfd00d1d664e76692a0f" + integrity sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A== + dependencies: + "@babel/types" "^7.24.7" + "@babel/helper-plugin-utils@7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" @@ -676,6 +796,15 @@ "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-wrap-function" "^7.22.20" +"@babel/helper-remap-async-to-generator@^7.24.7", "@babel/helper-remap-async-to-generator@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz#d2f0fbba059a42d68e5e378feaf181ef6055365e" + integrity sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.24.7" + "@babel/helper-wrap-function" "^7.25.0" + "@babel/traverse" "^7.25.0" + "@babel/helper-replace-supers@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz#7085bd19d4a0b7ed8f405c1ed73ccb70f323abc1" @@ -685,6 +814,15 @@ "@babel/helper-member-expression-to-functions" "^7.23.0" "@babel/helper-optimise-call-expression" "^7.22.5" +"@babel/helper-replace-supers@^7.24.7", "@babel/helper-replace-supers@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz#ff44deac1c9f619523fe2ca1fd650773792000a9" + integrity sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.24.8" + "@babel/helper-optimise-call-expression" "^7.24.7" + "@babel/traverse" "^7.25.0" + "@babel/helper-simple-access@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" @@ -714,6 +852,14 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-skip-transparent-expression-wrappers@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz#5f8fa83b69ed5c27adc56044f8be2b3ea96669d9" + integrity sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ== + dependencies: + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" + "@babel/helper-split-export-declaration@^7.22.6": version "7.22.6" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" @@ -765,7 +911,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== -"@babel/helper-validator-option@^7.24.8": +"@babel/helper-validator-option@^7.24.7", "@babel/helper-validator-option@^7.24.8": version "7.24.8" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz#3725cdeea8b480e86d34df15304806a06975e33d" integrity sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q== @@ -779,6 +925,15 @@ "@babel/template" "^7.22.15" "@babel/types" "^7.22.19" +"@babel/helper-wrap-function@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz#dab12f0f593d6ca48c0062c28bcfb14ebe812f81" + integrity sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ== + dependencies: + "@babel/template" "^7.25.0" + "@babel/traverse" "^7.25.0" + "@babel/types" "^7.25.0" + "@babel/helpers@^7.12.5", "@babel/helpers@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.1.tgz#183e44714b9eba36c3038e442516587b1e0a1a94" @@ -805,6 +960,14 @@ "@babel/template" "^7.24.7" "@babel/types" "^7.24.8" +"@babel/helpers@^7.25.0": + version "7.25.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.25.6.tgz#57ee60141829ba2e102f30711ffe3afab357cc60" + integrity sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q== + dependencies: + "@babel/template" "^7.25.0" + "@babel/types" "^7.25.6" + "@babel/highlight@^7.24.2": version "7.24.2" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.2.tgz#3f539503efc83d3c59080a10e6634306e0370d26" @@ -840,6 +1003,13 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.8.tgz#58a4dbbcad7eb1d48930524a3fd93d93e9084c6f" integrity sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w== +"@babel/parser@^7.25.0", "@babel/parser@^7.25.6": + version "7.25.6" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.25.6.tgz#85660c5ef388cbbf6e3d2a694ee97a38f18afe2f" + integrity sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q== + dependencies: + "@babel/types" "^7.25.6" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.24.5": version "7.24.5" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.5.tgz#4c3685eb9cd790bcad2843900fe0250c91ccf895" @@ -848,6 +1018,21 @@ "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-plugin-utils" "^7.24.5" +"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.3": + version "7.25.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz#dca427b45a6c0f5c095a1c639dfe2476a3daba7f" + integrity sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/traverse" "^7.25.3" + +"@babel/plugin-bugfix-safari-class-field-initializer-scope@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz#cd0c583e01369ef51676bdb3d7b603e17d2b3f73" + integrity sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.1.tgz#b645d9ba8c2bc5b7af50f0fe949f9edbeb07c8cf" @@ -855,6 +1040,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz#749bde80356b295390954643de7635e0dffabe73" + integrity sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.1.tgz#da8261f2697f0f41b0855b91d3a20a1fbfd271d3" @@ -864,6 +1056,15 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/plugin-transform-optional-chaining" "^7.24.1" +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz#e4eabdd5109acc399b38d7999b2ef66fc2022f89" + integrity sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" + "@babel/plugin-transform-optional-chaining" "^7.24.7" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.1.tgz#1181d9685984c91d657b8ddf14f0487a6bab2988" @@ -872,6 +1073,14 @@ "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz#3a82a70e7cb7294ad2559465ebcb871dfbf078fb" + integrity sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/traverse" "^7.25.0" + "@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.12.1", "@babel/plugin-proposal-class-properties@^7.4.4": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" @@ -1027,6 +1236,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-syntax-import-assertions@^7.24.7": + version "7.25.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.25.6.tgz#bb918905c58711b86f9710d74a3744b6c56573b5" + integrity sha512-aABl0jHw9bZ2karQ/uUD6XP4u0SG22SJrOHFoL6XB1R7dTovOP4TzTlsxOYC5yQ1pdscVK2JTUnF6QL3ARoAiQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/plugin-syntax-import-attributes@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.1.tgz#c66b966c63b714c4eec508fcf5763b1f2d381093" @@ -1034,6 +1250,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-syntax-import-attributes@^7.24.7": + version "7.25.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.6.tgz#6d4c78f042db0e82fd6436cd65fec5dc78ad2bde" + integrity sha512-sXaDXaJN9SNLymBdlWFA+bjzBhFD617ZaFiY13dGt7TVslVvVgA6fkZOP7Ki3IGElC45lwHdOTrCtKZGVAWeLQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" @@ -1069,7 +1292,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" -"@babel/plugin-syntax-jsx@^7.7.2": +"@babel/plugin-syntax-jsx@^7.24.7", "@babel/plugin-syntax-jsx@^7.7.2": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz#39a1fa4a7e3d3d7f34e2acc6be585b718d30e02d" integrity sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ== @@ -1139,6 +1362,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-syntax-typescript@^7.24.7": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.4.tgz#04db9ce5a9043d9c635e75ae7969a2cd50ca97ff" + integrity sha512-uMOCoHVU52BsSWxPOMVv5qKRdeSlPuImUCB2dlPuBSU+W2/ROE7/Zg8F2Kepbk+8yBa68LlRKxO+xgEVWorsDg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/plugin-syntax-typescript@^7.7.2": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz#58d458271b4d3b6bb27ee6ac9525acbb259bad1c" @@ -1161,6 +1391,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-arrow-functions@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz#4f6886c11e423bd69f3ce51dbf42424a5f275514" + integrity sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-async-generator-functions@^7.24.3": version "7.24.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.3.tgz#8fa7ae481b100768cc9842c8617808c5352b8b89" @@ -1171,6 +1408,16 @@ "@babel/helper-remap-async-to-generator" "^7.22.20" "@babel/plugin-syntax-async-generators" "^7.8.4" +"@babel/plugin-transform-async-generator-functions@^7.25.4": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.4.tgz#2afd4e639e2d055776c9f091b6c0c180ed8cf083" + integrity sha512-jz8cV2XDDTqjKPwVPJBIjORVEmSGYhdRa8e5k5+vN+uwcjSrSxUaebBRa4ko1jqNF2uxyg8G6XYk30Jv285xzg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/helper-remap-async-to-generator" "^7.25.0" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/traverse" "^7.25.4" + "@babel/plugin-transform-async-to-generator@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.1.tgz#0e220703b89f2216800ce7b1c53cb0cf521c37f4" @@ -1180,6 +1427,15 @@ "@babel/helper-plugin-utils" "^7.24.0" "@babel/helper-remap-async-to-generator" "^7.22.20" +"@babel/plugin-transform-async-to-generator@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz#72a3af6c451d575842a7e9b5a02863414355bdcc" + integrity sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA== + dependencies: + "@babel/helper-module-imports" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-remap-async-to-generator" "^7.24.7" + "@babel/plugin-transform-block-scoped-functions@^7.0.0", "@babel/plugin-transform-block-scoped-functions@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.1.tgz#1c94799e20fcd5c4d4589523bbc57b7692979380" @@ -1187,6 +1443,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-block-scoped-functions@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz#a4251d98ea0c0f399dafe1a35801eaba455bbf1f" + integrity sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-block-scoping@^7.0.0", "@babel/plugin-transform-block-scoping@^7.12.12", "@babel/plugin-transform-block-scoping@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.1.tgz#27af183d7f6dad890531256c7a45019df768ac1f" @@ -1201,6 +1464,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.5" +"@babel/plugin-transform-block-scoping@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz#23a6ed92e6b006d26b1869b1c91d1b917c2ea2ac" + integrity sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/plugin-transform-class-properties@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.1.tgz#bcbf1aef6ba6085cfddec9fc8d58871cf011fc29" @@ -1209,6 +1479,14 @@ "@babel/helper-create-class-features-plugin" "^7.24.1" "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-class-properties@^7.25.4": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.4.tgz#bae7dbfcdcc2e8667355cd1fb5eda298f05189fd" + integrity sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.25.4" + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/plugin-transform-class-static-block@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.1.tgz#4e37efcca1d9f2fcb908d1bae8b56b4b6e9e1cb6" @@ -1227,6 +1505,15 @@ "@babel/helper-plugin-utils" "^7.24.0" "@babel/plugin-syntax-class-static-block" "^7.14.5" +"@babel/plugin-transform-class-static-block@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz#c82027ebb7010bc33c116d4b5044fbbf8c05484d" + integrity sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-transform-classes@^7.0.0", "@babel/plugin-transform-classes@^7.12.1", "@babel/plugin-transform-classes@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.1.tgz#5bc8fc160ed96378184bc10042af47f50884dcb1" @@ -1255,6 +1542,18 @@ "@babel/helper-split-export-declaration" "^7.24.5" globals "^11.1.0" +"@babel/plugin-transform-classes@^7.25.4": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.4.tgz#d29dbb6a72d79f359952ad0b66d88518d65ef89a" + integrity sha512-oexUfaQle2pF/b6E0dwsxQtAol9TLSO88kQvym6HHBWFliV2lGdrPieX+WgMRLSJDVzdYywk7jXbLPuO2KLTLg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.24.7" + "@babel/helper-compilation-targets" "^7.25.2" + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/helper-replace-supers" "^7.25.0" + "@babel/traverse" "^7.25.4" + globals "^11.1.0" + "@babel/plugin-transform-computed-properties@^7.0.0", "@babel/plugin-transform-computed-properties@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.1.tgz#bc7e787f8e021eccfb677af5f13c29a9934ed8a7" @@ -1263,6 +1562,14 @@ "@babel/helper-plugin-utils" "^7.24.0" "@babel/template" "^7.24.0" +"@babel/plugin-transform-computed-properties@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz#4cab3214e80bc71fae3853238d13d097b004c707" + integrity sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/template" "^7.24.7" + "@babel/plugin-transform-destructuring@^7.0.0", "@babel/plugin-transform-destructuring@^7.12.1", "@babel/plugin-transform-destructuring@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.1.tgz#b1e8243af4a0206841973786292b8c8dd8447345" @@ -1277,6 +1584,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.5" +"@babel/plugin-transform-destructuring@^7.24.8": + version "7.24.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz#c828e814dbe42a2718a838c2a2e16a408e055550" + integrity sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/plugin-transform-dotall-regex@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.1.tgz#d56913d2f12795cc9930801b84c6f8c47513ac13" @@ -1285,6 +1599,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-dotall-regex@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz#5f8bf8a680f2116a7207e16288a5f974ad47a7a0" + integrity sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-duplicate-keys@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.1.tgz#5347a797fe82b8d09749d10e9f5b83665adbca88" @@ -1292,6 +1614,21 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-duplicate-keys@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz#dd20102897c9a2324e5adfffb67ff3610359a8ee" + integrity sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + +"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz#809af7e3339466b49c034c683964ee8afb3e2604" + integrity sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.25.0" + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/plugin-transform-dynamic-import@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.1.tgz#2a5a49959201970dd09a5fca856cb651e44439dd" @@ -1300,6 +1637,14 @@ "@babel/helper-plugin-utils" "^7.24.0" "@babel/plugin-syntax-dynamic-import" "^7.8.3" +"@babel/plugin-transform-dynamic-import@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz#4d8b95e3bae2b037673091aa09cd33fecd6419f4" + integrity sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-transform-exponentiation-operator@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.1.tgz#6650ebeb5bd5c012d5f5f90a26613a08162e8ba4" @@ -1308,6 +1653,14 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.15" "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-exponentiation-operator@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz#b629ee22645f412024297d5245bce425c31f9b0d" + integrity sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-export-namespace-from@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.1.tgz#f033541fc036e3efb2dcb58eedafd4f6b8078acd" @@ -1316,6 +1669,14 @@ "@babel/helper-plugin-utils" "^7.24.0" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" +"@babel/plugin-transform-export-namespace-from@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz#176d52d8d8ed516aeae7013ee9556d540c53f197" + integrity sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.24.1.tgz#fa8d0a146506ea195da1671d38eed459242b2dcc" @@ -1332,6 +1693,14 @@ "@babel/helper-plugin-utils" "^7.24.0" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" +"@babel/plugin-transform-for-of@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz#f25b33f72df1d8be76399e1b8f3f9d366eb5bc70" + integrity sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" + "@babel/plugin-transform-function-name@^7.0.0", "@babel/plugin-transform-function-name@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.1.tgz#8cba6f7730626cc4dfe4ca2fa516215a0592b361" @@ -1341,6 +1710,15 @@ "@babel/helper-function-name" "^7.23.0" "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-function-name@^7.25.1": + version "7.25.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz#b85e773097526c1a4fc4ba27322748643f26fc37" + integrity sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA== + dependencies: + "@babel/helper-compilation-targets" "^7.24.8" + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/traverse" "^7.25.1" + "@babel/plugin-transform-json-strings@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.1.tgz#08e6369b62ab3e8a7b61089151b161180c8299f7" @@ -1349,6 +1727,14 @@ "@babel/helper-plugin-utils" "^7.24.0" "@babel/plugin-syntax-json-strings" "^7.8.3" +"@babel/plugin-transform-json-strings@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz#f3e9c37c0a373fee86e36880d45b3664cedaf73a" + integrity sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-transform-literals@^7.0.0", "@babel/plugin-transform-literals@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.1.tgz#0a1982297af83e6b3c94972686067df588c5c096" @@ -1356,6 +1742,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-literals@^7.25.2": + version "7.25.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz#deb1ad14fc5490b9a65ed830e025bca849d8b5f3" + integrity sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/plugin-transform-logical-assignment-operators@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.1.tgz#719d8aded1aa94b8fb34e3a785ae8518e24cfa40" @@ -1364,6 +1757,14 @@ "@babel/helper-plugin-utils" "^7.24.0" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" +"@babel/plugin-transform-logical-assignment-operators@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz#a58fb6eda16c9dc8f9ff1c7b1ba6deb7f4694cb0" + integrity sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-transform-member-expression-literals@^7.0.0", "@babel/plugin-transform-member-expression-literals@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.1.tgz#896d23601c92f437af8b01371ad34beb75df4489" @@ -1371,6 +1772,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-member-expression-literals@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz#3b4454fb0e302e18ba4945ba3246acb1248315df" + integrity sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-modules-amd@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.1.tgz#b6d829ed15258536977e9c7cc6437814871ffa39" @@ -1379,6 +1787,14 @@ "@babel/helper-module-transforms" "^7.23.3" "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-modules-amd@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz#65090ed493c4a834976a3ca1cde776e6ccff32d7" + integrity sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg== + dependencies: + "@babel/helper-module-transforms" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.1.tgz#e71ba1d0d69e049a22bf90b3867e263823d3f1b9" @@ -1388,7 +1804,7 @@ "@babel/helper-plugin-utils" "^7.24.0" "@babel/helper-simple-access" "^7.22.5" -"@babel/plugin-transform-modules-commonjs@^7.24.8": +"@babel/plugin-transform-modules-commonjs@^7.24.7", "@babel/plugin-transform-modules-commonjs@^7.24.8": version "7.24.8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.8.tgz#ab6421e564b717cb475d6fff70ae7f103536ea3c" integrity sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA== @@ -1407,6 +1823,16 @@ "@babel/helper-plugin-utils" "^7.24.0" "@babel/helper-validator-identifier" "^7.22.20" +"@babel/plugin-transform-modules-systemjs@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz#8f46cdc5f9e5af74f3bd019485a6cbe59685ea33" + integrity sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw== + dependencies: + "@babel/helper-module-transforms" "^7.25.0" + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/helper-validator-identifier" "^7.24.7" + "@babel/traverse" "^7.25.0" + "@babel/plugin-transform-modules-umd@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.1.tgz#69220c66653a19cf2c0872b9c762b9a48b8bebef" @@ -1415,6 +1841,14 @@ "@babel/helper-module-transforms" "^7.23.3" "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-modules-umd@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz#edd9f43ec549099620df7df24e7ba13b5c76efc8" + integrity sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A== + dependencies: + "@babel/helper-module-transforms" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-named-capturing-groups-regex@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f" @@ -1423,6 +1857,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-named-capturing-groups-regex@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz#9042e9b856bc6b3688c0c2e4060e9e10b1460923" + integrity sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-new-target@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.1.tgz#29c59988fa3d0157de1c871a28cd83096363cc34" @@ -1430,6 +1872,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-new-target@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz#31ff54c4e0555cc549d5816e4ab39241dfb6ab00" + integrity sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-nullish-coalescing-operator@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.1.tgz#0cd494bb97cb07d428bd651632cb9d4140513988" @@ -1438,6 +1887,14 @@ "@babel/helper-plugin-utils" "^7.24.0" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" +"@babel/plugin-transform-nullish-coalescing-operator@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz#1de4534c590af9596f53d67f52a92f12db984120" + integrity sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-transform-numeric-separator@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.1.tgz#5bc019ce5b3435c1cadf37215e55e433d674d4e8" @@ -1446,6 +1903,14 @@ "@babel/helper-plugin-utils" "^7.24.0" "@babel/plugin-syntax-numeric-separator" "^7.10.4" +"@babel/plugin-transform-numeric-separator@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz#bea62b538c80605d8a0fac9b40f48e97efa7de63" + integrity sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-transform-object-rest-spread@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.1.tgz#5a3ce73caf0e7871a02e1c31e8b473093af241ff" @@ -1466,6 +1931,16 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-transform-parameters" "^7.24.5" +"@babel/plugin-transform-object-rest-spread@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz#d13a2b93435aeb8a197e115221cab266ba6e55d6" + integrity sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q== + dependencies: + "@babel/helper-compilation-targets" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.24.7" + "@babel/plugin-transform-object-super@^7.0.0", "@babel/plugin-transform-object-super@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.1.tgz#e71d6ab13483cca89ed95a474f542bbfc20a0520" @@ -1474,6 +1949,14 @@ "@babel/helper-plugin-utils" "^7.24.0" "@babel/helper-replace-supers" "^7.24.1" +"@babel/plugin-transform-object-super@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz#66eeaff7830bba945dd8989b632a40c04ed625be" + integrity sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-replace-supers" "^7.24.7" + "@babel/plugin-transform-optional-catch-binding@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.1.tgz#92a3d0efe847ba722f1a4508669b23134669e2da" @@ -1482,6 +1965,14 @@ "@babel/helper-plugin-utils" "^7.24.0" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" +"@babel/plugin-transform-optional-catch-binding@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz#00eabd883d0dd6a60c1c557548785919b6e717b4" + integrity sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-transform-optional-chaining@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.1.tgz#26e588acbedce1ab3519ac40cc748e380c5291e6" @@ -1500,6 +1991,15 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/plugin-syntax-optional-chaining" "^7.8.3" +"@babel/plugin-transform-optional-chaining@^7.24.7", "@babel/plugin-transform-optional-chaining@^7.24.8": + version "7.24.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz#bb02a67b60ff0406085c13d104c99a835cdf365d" + integrity sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.12.1", "@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.1.tgz#983c15d114da190506c75b616ceb0f817afcc510" @@ -1514,6 +2014,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.5" +"@babel/plugin-transform-parameters@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz#5881f0ae21018400e320fc7eb817e529d1254b68" + integrity sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-private-methods@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.1.tgz#a0faa1ae87eff077e1e47a5ec81c3aef383dc15a" @@ -1522,6 +2029,14 @@ "@babel/helper-create-class-features-plugin" "^7.24.1" "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-private-methods@^7.25.4": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.4.tgz#9bbefbe3649f470d681997e0b64a4b254d877242" + integrity sha512-ao8BG7E2b/URaUQGqN3Tlsg+M3KlHY6rJ1O1gXAEUnZoyNQnvKyH87Kfg+FoxSeyWUB8ISZZsC91C44ZuBFytw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.25.4" + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/plugin-transform-private-property-in-object@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.1.tgz#756443d400274f8fb7896742962cc1b9f25c1f6a" @@ -1542,6 +2057,16 @@ "@babel/helper-plugin-utils" "^7.24.5" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" +"@babel/plugin-transform-private-property-in-object@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz#4eec6bc701288c1fab5f72e6a4bbc9d67faca061" + integrity sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.24.7" + "@babel/helper-create-class-features-plugin" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-transform-property-literals@^7.0.0", "@babel/plugin-transform-property-literals@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.1.tgz#d6a9aeab96f03749f4eebeb0b6ea8e90ec958825" @@ -1549,6 +2074,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-property-literals@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz#f0d2ed8380dfbed949c42d4d790266525d63bbdc" + integrity sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-react-constant-elements@^7.18.12", "@babel/plugin-transform-react-constant-elements@^7.21.3": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.24.1.tgz#d493a0918b9fdad7540f5afd9b5eb5c52500d18d" @@ -1597,6 +2129,14 @@ "@babel/helper-plugin-utils" "^7.24.0" regenerator-transform "^0.15.2" +"@babel/plugin-transform-regenerator@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz#021562de4534d8b4b1851759fd7af4e05d2c47f8" + integrity sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + regenerator-transform "^0.15.2" + "@babel/plugin-transform-reserved-words@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.1.tgz#8de729f5ecbaaf5cf83b67de13bad38a21be57c1" @@ -1604,6 +2144,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-reserved-words@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz#80037fe4fbf031fc1125022178ff3938bb3743a4" + integrity sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-runtime@^7.22.9": version "7.24.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.3.tgz#dc58ad4a31810a890550365cc922e1ff5acb5d7f" @@ -1623,6 +2170,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-shorthand-properties@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz#85448c6b996e122fa9e289746140aaa99da64e73" + integrity sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-spread@^7.0.0", "@babel/plugin-transform-spread@^7.12.1", "@babel/plugin-transform-spread@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.1.tgz#a1acf9152cbf690e4da0ba10790b3ac7d2b2b391" @@ -1631,6 +2185,14 @@ "@babel/helper-plugin-utils" "^7.24.0" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" +"@babel/plugin-transform-spread@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz#e8a38c0fde7882e0fb8f160378f74bd885cc7bb3" + integrity sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" + "@babel/plugin-transform-sticky-regex@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.1.tgz#f03e672912c6e203ed8d6e0271d9c2113dc031b9" @@ -1638,6 +2200,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-sticky-regex@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz#96ae80d7a7e5251f657b5cf18f1ea6bf926f5feb" + integrity sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-template-literals@^7.0.0", "@babel/plugin-transform-template-literals@^7.12.1", "@babel/plugin-transform-template-literals@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.1.tgz#15e2166873a30d8617e3e2ccadb86643d327aab7" @@ -1645,6 +2214,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-template-literals@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz#a05debb4a9072ae8f985bcf77f3f215434c8f8c8" + integrity sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-typeof-symbol@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.1.tgz#6831f78647080dec044f7e9f68003d99424f94c7" @@ -1659,6 +2235,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.5" +"@babel/plugin-transform-typeof-symbol@^7.24.8": + version "7.24.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz#383dab37fb073f5bfe6e60c654caac309f92ba1c" + integrity sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/plugin-transform-typescript@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.1.tgz#5c05e28bb76c7dfe7d6c5bed9951324fd2d3ab07" @@ -1669,6 +2252,17 @@ "@babel/helper-plugin-utils" "^7.24.0" "@babel/plugin-syntax-typescript" "^7.24.1" +"@babel/plugin-transform-typescript@^7.24.7": + version "7.25.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.2.tgz#237c5d10de6d493be31637c6b9fa30b6c5461add" + integrity sha512-lBwRvjSmqiMYe/pS0+1gggjJleUJi7NzjvQ1Fkqtt69hBa/0t1YuW/MLQMAPixfwaQOHUXsd6jeU3Z+vdGv3+A== + dependencies: + "@babel/helper-annotate-as-pure" "^7.24.7" + "@babel/helper-create-class-features-plugin" "^7.25.0" + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" + "@babel/plugin-syntax-typescript" "^7.24.7" + "@babel/plugin-transform-unicode-escapes@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.1.tgz#fb3fa16676549ac7c7449db9b342614985c2a3a4" @@ -1676,6 +2270,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-unicode-escapes@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz#2023a82ced1fb4971630a2e079764502c4148e0e" + integrity sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-unicode-property-regex@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.1.tgz#56704fd4d99da81e5e9f0c0c93cabd91dbc4889e" @@ -1684,6 +2285,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-unicode-property-regex@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz#9073a4cd13b86ea71c3264659590ac086605bbcd" + integrity sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-unicode-regex@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.1.tgz#57c3c191d68f998ac46b708380c1ce4d13536385" @@ -1692,6 +2301,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-unicode-regex@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz#dfc3d4a51127108099b19817c0963be6a2adf19f" + integrity sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-unicode-sets-regex@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.1.tgz#c1ea175b02afcffc9cf57a9c4658326625165b7f" @@ -1700,6 +2317,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.24.0" +"@babel/plugin-transform-unicode-sets-regex@^7.25.4": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.4.tgz#be664c2a0697ffacd3423595d5edef6049e8946c" + integrity sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.25.2" + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.11": version "7.24.3" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.24.3.tgz#f3f138c844ffeeac372597b29c51b5259e8323a3" @@ -1873,6 +2498,95 @@ core-js-compat "^3.31.0" semver "^6.3.1" +"@babel/preset-env@^7.25.4": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.25.4.tgz#be23043d43a34a2721cd0f676c7ba6f1481f6af6" + integrity sha512-W9Gyo+KmcxjGahtt3t9fb14vFRWvPpu5pT6GBlovAK6BTBcxgjfVMSQCfJl4oi35ODrxP6xx2Wr8LNST57Mraw== + dependencies: + "@babel/compat-data" "^7.25.4" + "@babel/helper-compilation-targets" "^7.25.2" + "@babel/helper-plugin-utils" "^7.24.8" + "@babel/helper-validator-option" "^7.24.8" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.25.3" + "@babel/plugin-bugfix-safari-class-field-initializer-scope" "^7.25.0" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.25.0" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.24.7" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.25.0" + "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.24.7" + "@babel/plugin-syntax-import-attributes" "^7.24.7" + "@babel/plugin-syntax-import-meta" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" + "@babel/plugin-transform-arrow-functions" "^7.24.7" + "@babel/plugin-transform-async-generator-functions" "^7.25.4" + "@babel/plugin-transform-async-to-generator" "^7.24.7" + "@babel/plugin-transform-block-scoped-functions" "^7.24.7" + "@babel/plugin-transform-block-scoping" "^7.25.0" + "@babel/plugin-transform-class-properties" "^7.25.4" + "@babel/plugin-transform-class-static-block" "^7.24.7" + "@babel/plugin-transform-classes" "^7.25.4" + "@babel/plugin-transform-computed-properties" "^7.24.7" + "@babel/plugin-transform-destructuring" "^7.24.8" + "@babel/plugin-transform-dotall-regex" "^7.24.7" + "@babel/plugin-transform-duplicate-keys" "^7.24.7" + "@babel/plugin-transform-duplicate-named-capturing-groups-regex" "^7.25.0" + "@babel/plugin-transform-dynamic-import" "^7.24.7" + "@babel/plugin-transform-exponentiation-operator" "^7.24.7" + "@babel/plugin-transform-export-namespace-from" "^7.24.7" + "@babel/plugin-transform-for-of" "^7.24.7" + "@babel/plugin-transform-function-name" "^7.25.1" + "@babel/plugin-transform-json-strings" "^7.24.7" + "@babel/plugin-transform-literals" "^7.25.2" + "@babel/plugin-transform-logical-assignment-operators" "^7.24.7" + "@babel/plugin-transform-member-expression-literals" "^7.24.7" + "@babel/plugin-transform-modules-amd" "^7.24.7" + "@babel/plugin-transform-modules-commonjs" "^7.24.8" + "@babel/plugin-transform-modules-systemjs" "^7.25.0" + "@babel/plugin-transform-modules-umd" "^7.24.7" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.24.7" + "@babel/plugin-transform-new-target" "^7.24.7" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.24.7" + "@babel/plugin-transform-numeric-separator" "^7.24.7" + "@babel/plugin-transform-object-rest-spread" "^7.24.7" + "@babel/plugin-transform-object-super" "^7.24.7" + "@babel/plugin-transform-optional-catch-binding" "^7.24.7" + "@babel/plugin-transform-optional-chaining" "^7.24.8" + "@babel/plugin-transform-parameters" "^7.24.7" + "@babel/plugin-transform-private-methods" "^7.25.4" + "@babel/plugin-transform-private-property-in-object" "^7.24.7" + "@babel/plugin-transform-property-literals" "^7.24.7" + "@babel/plugin-transform-regenerator" "^7.24.7" + "@babel/plugin-transform-reserved-words" "^7.24.7" + "@babel/plugin-transform-shorthand-properties" "^7.24.7" + "@babel/plugin-transform-spread" "^7.24.7" + "@babel/plugin-transform-sticky-regex" "^7.24.7" + "@babel/plugin-transform-template-literals" "^7.24.7" + "@babel/plugin-transform-typeof-symbol" "^7.24.8" + "@babel/plugin-transform-unicode-escapes" "^7.24.7" + "@babel/plugin-transform-unicode-property-regex" "^7.24.7" + "@babel/plugin-transform-unicode-regex" "^7.24.7" + "@babel/plugin-transform-unicode-sets-regex" "^7.25.4" + "@babel/preset-modules" "0.1.6-no-external-plugins" + babel-plugin-polyfill-corejs2 "^0.4.10" + babel-plugin-polyfill-corejs3 "^0.10.6" + babel-plugin-polyfill-regenerator "^0.6.1" + core-js-compat "^3.37.1" + semver "^6.3.1" + "@babel/preset-flow@^7.12.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.24.1.tgz#da7196c20c2d7dd4e98cfd8b192fe53b5eb6f0bb" @@ -1914,6 +2628,17 @@ "@babel/plugin-transform-modules-commonjs" "^7.24.1" "@babel/plugin-transform-typescript" "^7.24.1" +"@babel/preset-typescript@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.24.7.tgz#66cd86ea8f8c014855671d5ea9a737139cbbfef1" + integrity sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-validator-option" "^7.24.7" + "@babel/plugin-syntax-jsx" "^7.24.7" + "@babel/plugin-transform-modules-commonjs" "^7.24.7" + "@babel/plugin-transform-typescript" "^7.24.7" + "@babel/register@^7.12.1": version "7.23.7" resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.23.7.tgz#485a5e7951939d21304cae4af1719fdb887bc038" @@ -1998,6 +2723,15 @@ "@babel/parser" "^7.24.7" "@babel/types" "^7.24.7" +"@babel/template@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.0.tgz#e733dc3134b4fede528c15bc95e89cb98c52592a" + integrity sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q== + dependencies: + "@babel/code-frame" "^7.24.7" + "@babel/parser" "^7.25.0" + "@babel/types" "^7.25.0" + "@babel/traverse@^7.1.0", "@babel/traverse@^7.1.6", "@babel/traverse@^7.11.5", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.14.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.23.2", "@babel/traverse@^7.24.1", "@babel/traverse@^7.7.0": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.1.tgz#d65c36ac9dd17282175d1e4a3c49d5b7988f530c" @@ -2046,6 +2780,19 @@ debug "^4.3.1" globals "^11.1.0" +"@babel/traverse@^7.25.0", "@babel/traverse@^7.25.1", "@babel/traverse@^7.25.2", "@babel/traverse@^7.25.3", "@babel/traverse@^7.25.4": + version "7.25.6" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.6.tgz#04fad980e444f182ecf1520504941940a90fea41" + integrity sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ== + dependencies: + "@babel/code-frame" "^7.24.7" + "@babel/generator" "^7.25.6" + "@babel/parser" "^7.25.6" + "@babel/template" "^7.25.0" + "@babel/types" "^7.25.6" + debug "^4.3.1" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.2.0", "@babel/types@^7.20.7", "@babel/types@^7.22.10", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.24.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": version "7.24.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.0.tgz#3b951f435a92e7333eba05b7566fd297960ea1bf" @@ -2073,6 +2820,15 @@ "@babel/helper-validator-identifier" "^7.24.7" to-fast-properties "^2.0.0" +"@babel/types@^7.25.0", "@babel/types@^7.25.2", "@babel/types@^7.25.6": + version "7.25.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.6.tgz#893942ddb858f32ae7a004ec9d3a76b3463ef8e6" + integrity sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw== + dependencies: + "@babel/helper-string-parser" "^7.24.8" + "@babel/helper-validator-identifier" "^7.24.7" + to-fast-properties "^2.0.0" + "@base2/pretty-print-object@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@base2/pretty-print-object/-/pretty-print-object-1.0.1.tgz#371ba8be66d556812dc7fb169ebc3c08378f69d4" @@ -10286,6 +11042,14 @@ jest-diff "^25.2.1" pretty-format "^25.2.1" +"@types/jest@^29.5.13": + version "29.5.13" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.13.tgz#8bc571659f401e6a719a7bf0dbcb8b78c71a8adc" + integrity sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + "@types/js-yaml@^4.0.0": version "4.0.9" resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.9.tgz#cd82382c4f902fed9691a2ed79ec68c5898af4c2" @@ -12928,6 +13692,11 @@ async@3.2.1: resolved "https://registry.yarnpkg.com/async/-/async-3.2.1.tgz#d3274ec66d107a47476a4c49136aacdb00665fc8" integrity sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg== +async@^3.2.3: + version "3.2.6" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" + integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== + async@^3.2.4: version "3.2.5" resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" @@ -13214,6 +13983,14 @@ babel-plugin-polyfill-corejs3@^0.10.1, babel-plugin-polyfill-corejs3@^0.10.4: "@babel/helper-define-polyfill-provider" "^0.6.1" core-js-compat "^3.36.1" +babel-plugin-polyfill-corejs3@^0.10.6: + version "0.10.6" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz#2deda57caef50f59c525aeb4964d3b2f867710c7" + integrity sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.6.2" + core-js-compat "^3.38.0" + babel-plugin-polyfill-regenerator@^0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.0.4.tgz#588641af9a2cb4e299b1400c47672a4a104d2459" @@ -13811,7 +14588,17 @@ browserslist@^4.23.1: node-releases "^2.0.14" update-browserslist-db "^1.1.0" -bs-logger@0.x: +browserslist@^4.23.3: + version "4.23.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.3.tgz#debb029d3c93ebc97ffbc8d9cbb03403e227c800" + integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA== + dependencies: + caniuse-lite "^1.0.30001646" + electron-to-chromium "^1.5.4" + node-releases "^2.0.18" + update-browserslist-db "^1.1.0" + +bs-logger@0.x, bs-logger@^0.2.6: version "0.2.6" resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== @@ -14240,6 +15027,11 @@ caniuse-lite@^1.0.30001640: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz#9c004caef315de9452ab970c3da71085f8241dbd" integrity sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg== +caniuse-lite@^1.0.30001646: + version "1.0.30001660" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz#31218de3463fabb44d0b7607b652e56edf2e2355" + integrity sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg== + capital-case@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669" @@ -14322,7 +15114,7 @@ chai@^4.2.0: pathval "^1.1.1" type-detect "^4.0.8" -chalk@4.1.2, chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: +chalk@4.1.2, chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -15478,6 +16270,13 @@ core-js-compat@^3.31.0, core-js-compat@^3.36.1, core-js-compat@^3.8.1: dependencies: browserslist "^4.23.0" +core-js-compat@^3.37.1, core-js-compat@^3.38.0: + version "3.38.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.38.1.tgz#2bc7a298746ca5a7bcb9c164bcb120f2ebc09a09" + integrity sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw== + dependencies: + browserslist "^4.23.3" + core-js-pure@^3.23.3: version "3.32.0" resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.32.0.tgz#5d79f85da7a4373e9a06494ccbef995a4c639f8b" @@ -17213,6 +18012,13 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== +ejs@^3.1.10: + version "3.1.10" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" + integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== + dependencies: + jake "^10.8.5" + electron-to-chromium@^1.4.668: version "1.4.714" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.714.tgz#708fdc8d5bdec824e41fe8b1b0e10af508a10946" @@ -17223,6 +18029,11 @@ electron-to-chromium@^1.4.820: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.0.tgz#0d3123a9f09189b9c7ab4b5d6848d71b3c1fd0e8" integrity sha512-Vb3xHHYnLseK8vlMJQKJYXJ++t4u1/qJ3vykuVrVjvdiOEhYyT1AuP4x03G8EnPmYvYOhe9T+dADTmthjRQMkA== +electron-to-chromium@^1.5.4: + version "1.5.23" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.23.tgz#6dabd8f7fec5cbf618b732ff4c42950dcc7a3be5" + integrity sha512-mBhODedOXg4v5QWwl21DjM5amzjmI1zw9EPrPK/5Wx7C8jt33bpZNrC7OhHUG3pxRtbLpr3W2dXT+Ph1SsfRZA== + elliptic@6.5.4: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" @@ -19060,6 +19871,14 @@ expect@^29.7.0: jest-message-util "^29.7.0" jest-util "^29.7.0" +express-validator@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/express-validator/-/express-validator-7.2.0.tgz#f6077758732d52e2365bb983b1abaca51bbefba6" + integrity sha512-I2ByKD8panjtr8Y05l21Wph9xk7kk64UMyvJCl/fFM/3CTJq8isXYPLeKW/aZBCdb/LYNv63PwhY8khw8VWocA== + dependencies: + lodash "^4.17.21" + validator "~13.12.0" + express@^4.14.0, express@^4.17.1, express@^4.18.2: version "4.19.1" resolved "https://registry.yarnpkg.com/express/-/express-4.19.1.tgz#4700635795e911600a45596138cf5b0320e78256" @@ -19494,6 +20313,13 @@ file-uri-to-path@1.0.0: resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== +filelist@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" + integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== + dependencies: + minimatch "^5.0.1" + filename-reserved-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229" @@ -23064,6 +23890,16 @@ jackspeak@^2.3.5: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" +jake@^10.8.5: + version "10.9.2" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.2.tgz#6ae487e6a69afec3a5e167628996b59f35ae2b7f" + integrity sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA== + dependencies: + async "^3.2.3" + chalk "^4.0.2" + filelist "^1.0.4" + minimatch "^3.1.2" + jest-changed-files@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-25.5.0.tgz#141cc23567ceb3f534526f8614ba39421383634c" @@ -24999,7 +25835,7 @@ make-dir@^4.0.0: dependencies: semver "^7.5.3" -make-error@1.x, make-error@^1.1.1: +make-error@1.x, make-error@^1.1.1, make-error@^1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== @@ -27444,6 +28280,11 @@ node-releases@^2.0.14: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== +node-releases@^2.0.18: + version "2.0.18" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== + nodemon@^3.0.1: version "3.1.0" resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.1.0.tgz#ff7394f2450eb6a5e96fe4180acd5176b29799c9" @@ -32288,6 +33129,11 @@ semver@^7.1.1, semver@^7.1.3, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semve dependencies: lru-cache "^6.0.0" +semver@^7.6.3: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + send@0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" @@ -34824,6 +35670,21 @@ ts-jest@^29.0.5: semver "^7.5.3" yargs-parser "^21.0.1" +ts-jest@^29.2.5: + version "29.2.5" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.2.5.tgz#591a3c108e1f5ebd013d3152142cb5472b399d63" + integrity sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA== + dependencies: + bs-logger "^0.2.6" + ejs "^3.1.10" + fast-json-stable-stringify "^2.1.0" + jest-util "^29.0.0" + json5 "^2.2.3" + lodash.memoize "^4.1.2" + make-error "^1.3.6" + semver "^7.6.3" + yargs-parser "^21.1.1" + ts-loader@^9.5.1: version "9.5.1" resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.5.1.tgz#63d5912a86312f1fbe32cef0859fb8b2193d9b89" @@ -34909,6 +35770,25 @@ ts-node@7.0.1: source-map-support "^0.5.6" yn "^2.0.0" +ts-node@^10.9.2: + version "10.9.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + ts-pnp@^1.1.6: version "1.2.0" resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" @@ -36179,6 +37059,11 @@ validate.io-number@^1.0.3: resolved "https://registry.yarnpkg.com/validate.io-number/-/validate.io-number-1.0.3.tgz#f63ffeda248bf28a67a8d48e0e3b461a1665baf8" integrity sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg== +validator@~13.12.0: + version "13.12.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.12.0.tgz#7d78e76ba85504da3fee4fd1922b385914d4b35f" + integrity sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg== + valtio@1.11.2: version "1.11.2" resolved "https://registry.yarnpkg.com/valtio/-/valtio-1.11.2.tgz#b8049c02dfe65620635d23ebae9121a741bb6530"