diff --git a/.github/workflows/solidity.yml b/.github/workflows/solidity.yml index 499aa0bf98..19d0d643a1 100644 --- a/.github/workflows/solidity.yml +++ b/.github/workflows/solidity.yml @@ -110,7 +110,6 @@ jobs: env: VERCEL_PROJECT_ID: ${{ steps.project_id.outputs.VERCEL_PROJECT_ID}} - cancel-outdated: name: Cancel Outdated Jobs runs-on: ubuntu-latest @@ -130,6 +129,9 @@ jobs: fail-fast: false matrix: package: ${{ fromJson(needs.changes.outputs.packages) }} + # Slither is irrelevant for solidity-devops, as it only contains devops scripts rather than deployed contracts + exclude: + - package: solidity-devops permissions: # always required security-events: write @@ -138,32 +140,27 @@ jobs: contents: read steps: - name: Checkout repository - if: ${{ matrix.package != 'solidity-devops' }} uses: actions/checkout@v4 with: fetch-depth: 2 submodules: 'recursive' - name: Setup NodeJS - if: ${{ matrix.package != 'solidity-devops' }} uses: ./.github/actions/setup-nodejs - name: Install Foundry - if: ${{ matrix.package != 'solidity-devops' }} uses: foundry-rs/foundry-toolchain@v1 with: version: nightly # TODO: find a flag for this - name: Delete Untested Files - if: ${{ matrix.package != 'solidity-devops' }} working-directory: './packages/${{matrix.package}}' run: | rm -rf test/ || true rm -rf script/ || true - name: Build Contracts - if: ${{ matrix.package != 'solidity-devops' }} # TODO: unforunately, as of now concurrency needs to be 1 since if multiple instances of forge try to install the same version # of solc at the same time, we get "text file busy" errors. See https://github.com/synapsecns/sanguine/actions/runs/8457116792/job/23168392860?pr=2234 # for an example. @@ -171,7 +168,6 @@ jobs: npx lerna exec npm run build:slither --concurrency=1 - name: Run Slither - if: ${{ matrix.package != 'solidity-devops' }} uses: crytic/slither-action@v0.3.0 continue-on-error: true id: slither @@ -183,7 +179,7 @@ jobs: solc-version: 0.8.17 - name: Upload SARIF file - if: ${{!github.event.repository.private && matrix.package != 'solidity-devops'}} + if: ${{!github.event.repository.private}} uses: github/codeql-action/upload-sarif@v2 with: sarif_file: ./results.sarif @@ -199,28 +195,32 @@ jobs: package: ${{ fromJson(needs.changes.outputs.packages) }} steps: - uses: actions/checkout@v4 - if: ${{ matrix.package != 'solidity-devops' }} with: submodules: recursive - name: Setup Node JS - if: ${{ matrix.package != 'solidity-devops' }} uses: ./.github/actions/setup-nodejs - name: Installing dependencies - if: ${{ matrix.package != 'solidity-devops' }} run: yarn install --immutable - name: Install Foundry - if: ${{ matrix.package != 'solidity-devops' }} uses: foundry-rs/foundry-toolchain@v1 with: version: nightly - name: Run Foundry Tests + working-directory: './packages/${{matrix.package}}' + run: forge test -vvv + + # We skip only the coverage steps for solidity-devops before we establish a good coverage there + - name: Run Foundry Coverage if: ${{ matrix.package != 'solidity-devops' }} working-directory: './packages/${{matrix.package}}' - run: forge coverage -vvv --report lcov --report summary >> $GITHUB_STEP_SUMMARY + run: forge coverage --report lcov --report summary >> $GITHUB_STEP_SUMMARY + # Limit the number of fuzz runs to speed up the coverage + env: + FOUNDRY_FUZZ_RUNS: 10 - name: Send Coverage (Codecov) if: ${{ matrix.package != 'solidity-devops' }} @@ -236,39 +236,57 @@ jobs: attempt_limit: 5 attempt_delay: 30000 - - snapshot: + gas-diff: runs-on: ubuntu-latest - name: Foundry Gas Snapshot + name: Foundry Gas Diff if: ${{ needs.changes.outputs.package_count > 0 }} needs: changes strategy: fail-fast: false matrix: package: ${{ fromJson(needs.changes.outputs.packages) }} + # Gas diff is irrelevant for solidity-devops, as it only contains devops scripts rather than deployed contracts + exclude: + - package: solidity-devops steps: - uses: actions/checkout@v4 - if: ${{ matrix.package != 'solidity-devops' }} with: submodules: recursive - name: Setup Node JS - if: ${{ matrix.package != 'solidity-devops' }} uses: ./.github/actions/setup-nodejs - name: Installing dependencies - if: ${{ matrix.package != 'solidity-devops' }} run: yarn install --immutable - name: Install Foundry - if: ${{ matrix.package != 'solidity-devops' }} uses: foundry-rs/foundry-toolchain@v1 with: version: nightly - - name: Run snapshot - if: ${{ matrix.package != 'solidity-devops' }} + + - name: Run tests and generate gas report working-directory: './packages/${{matrix.package}}' - run: forge snapshot >> $GITHUB_STEP_SUMMARY + # Run separate set of tests (no fuzzing) to get accurate average gas cost estimates + run: forge test --mc GasBenchmark --gas-report > "../../gas-report-${{ matrix.package }}.ansi" + + - name: Compare gas reports + uses: Rubilmax/foundry-gas-diff@v3.18 + with: + ignore: 'test/**/*' + report: 'gas-report-${{ matrix.package }}.ansi' + sortCriteria: avg + sortOrders: desc + summaryQuantile: 0.5 + id: gas_diff + + - name: Add gas diff to sticky comment + if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }} + uses: marocchino/sticky-pull-request-comment@v2 + with: + # delete the comment in case changes no longer impact gas costs + delete: ${{ !steps.gas_diff.outputs.markdown }} + message: ${{ steps.gas_diff.outputs.markdown }} + size-check: name: Foundry Size Check runs-on: ubuntu-latest @@ -278,25 +296,24 @@ jobs: fail-fast: false matrix: package: ${{ fromJson(needs.changes.outputs.packages) }} + # Size check is irrelevant for solidity-devops, as it only contains devops scripts rather than deployed contracts + exclude: + - package: solidity-devops steps: - uses: actions/checkout@v4 - if: ${{ matrix.package != 'solidity-devops' }} with: submodules: recursive - name: Setup Node JS - if: ${{ matrix.package != 'solidity-devops' }} uses: ./.github/actions/setup-nodejs - name: Install Foundry - if: ${{ matrix.package != 'solidity-devops' }} uses: foundry-rs/foundry-toolchain@v1 with: version: nightly # This will run https://book.getfoundry.sh/reference/forge/forge-build#build-options - name: Run forge build --sizes - if: ${{ matrix.package != 'solidity-devops' }} run: | forge build --sizes working-directory: './packages/${{matrix.package}}' diff --git a/.vscode/settings.json b/.vscode/settings.json index 116a5a5174..e754f0e0f5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,8 @@ { "editor.fontLigatures": "'calt', 'liga', 'ss01', 'ss02', 'ss03', 'ss04', 'ss05', 'ss06', 'ss07', 'ss08', 'ss09'", + "[solidity]": { + "editor.defaultFormatter": "JuanBlanco.solidity" + }, "[typescript]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, @@ -16,11 +19,8 @@ "eslint.format.enable": true, "editorconfig.generateAuto": false, "files.trimTrailingWhitespace": true, - "solidity.packageDefaultDependenciesContractsDirectory": "contracts", - "solidity.packageDefaultDependenciesDirectory": "lib", - "solidity.compileUsingRemoteVersion": "v0.8.17+commit.8df45f5f", - "solidity.formatter": "prettier", - "solidity.defaultCompiler": "remote", + "solidity.formatter": "forge", + "solidity.monoRepoSupport": true, "tailwindCSS.classAttributes": [ ".*class.*", ".*Class.*", diff --git a/README.md b/README.md index 35bfaeef94..4a9b769013 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ root │ ├── coverage-aggregator: Javascript coverage aggregator based on nyc │ ├── docs: Docasaurus documentation. Note: this is not yet in use, and docs are still maintained on gitbook │ ├── explorer-ui: Explorer UI +│ ├── rfq-indexer: RFQ indexer │ ├── rest-api: Rest API │ ├── sdk-router: SDK router │ ├── solidity-devops: provides a set of tools and scripts to help with the development of Solidity smart contracts diff --git a/contrib/opbot/botmd/commands.go b/contrib/opbot/botmd/commands.go index 5dc95b8403..b72f4ff7c2 100644 --- a/contrib/opbot/botmd/commands.go +++ b/contrib/opbot/botmd/commands.go @@ -25,6 +25,7 @@ import ( "github.com/synapsecns/sanguine/core/retry" "github.com/synapsecns/sanguine/ethergo/chaindata" "github.com/synapsecns/sanguine/ethergo/client" + "github.com/synapsecns/sanguine/ethergo/submitter" rfqClient "github.com/synapsecns/sanguine/services/rfq/api/client" "github.com/synapsecns/sanguine/services/rfq/contracts/fastbridge" "github.com/synapsecns/sanguine/services/rfq/relayer/relapi" @@ -347,17 +348,12 @@ func (b *Bot) rfqRefund() *slacker.CommandDefinition { return } - var txHash *relapi.TxHashByNonceResponse + var status submitter.SubmissionStatus err = retry.WithBackoff( ctx.Context(), func(ctx context.Context) error { - txHash, err = relClient.GetTxHashByNonce( - ctx, - &relapi.GetTxByNonceRequest{ - ChainID: rawRequest.OriginChainID, - Nonce: nonce, - }) - if err != nil { + status, err = b.submitter.GetSubmissionStatus(ctx, big.NewInt(int64(rawRequest.OriginChainID)), nonce) + if err != nil || !status.HasTx() { b.logger.Errorf(ctx, "error fetching quote request: %v", err) return fmt.Errorf("error fetching quote request: %w", err) } @@ -373,7 +369,7 @@ func (b *Bot) rfqRefund() *slacker.CommandDefinition { return } - _, err = ctx.Response().Reply(fmt.Sprintf("refund submitted: %s", toExplorerSlackLink(txHash.Hash))) + _, err = ctx.Response().Reply(fmt.Sprintf("refund submitted: %s", toExplorerSlackLink(status.TxHash().String()))) if err != nil { log.Println(err) } diff --git a/contrib/opbot/go.mod b/contrib/opbot/go.mod index 71ec290d1a..70e9afa62e 100644 --- a/contrib/opbot/go.mod +++ b/contrib/opbot/go.mod @@ -210,6 +210,7 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.54.0 // indirect github.com/prometheus/procfs v0.15.0 // indirect + github.com/puzpuzpuz/xsync v1.5.2 // indirect github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect github.com/richardwilkes/toolbox v1.74.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect diff --git a/contrib/promexporter/exporters/exporter.go b/contrib/promexporter/exporters/exporter.go index d67c42fd8d..df8d4af7e4 100644 --- a/contrib/promexporter/exporters/exporter.go +++ b/contrib/promexporter/exporters/exporter.go @@ -142,17 +142,17 @@ func (e *exporter) collectMetrics(parentCtx context.Context) (err error) { for _, pending := range e.cfg.DFKPending { if err := e.stuckHeroCountStats(ctx, common.HexToAddress(pending.Owner), pending.ChainName); err != nil { errs = append(errs, fmt.Errorf("could not get stuck hero count: %w", err)) + span.AddEvent("could not get stuck hero count") } - span.AddEvent("could not get stuck hero count") } for _, gasCheck := range e.cfg.SubmitterChecks { for _, chainID := range gasCheck.ChainIDs { if err := e.submitterStats(common.HexToAddress(gasCheck.Address), chainID, gasCheck.Name); err != nil { - errs = append(errs, fmt.Errorf("could setup metric: %w", err)) + errs = append(errs, fmt.Errorf("could not get submitter stats: %w", err)) + span.AddEvent("could not get submitter stats") } } - span.AddEvent("could get submitter stats") } for chainID := range e.cfg.BridgeChecks { @@ -161,7 +161,8 @@ func (e *exporter) collectMetrics(parentCtx context.Context) (err error) { return retry.WithBackoff(ctx, func(ctx context.Context) error { err := e.vpriceStats(ctx, chainID, token) if err != nil && !errors.Is(err, errPoolNotExist) { - errs = append(errs, fmt.Errorf("stuck hero stats: %w", err)) + errs = append(errs, fmt.Errorf("could not get vprice %w", err)) + span.AddEvent("could not get vprice stats") } return nil @@ -172,7 +173,7 @@ func (e *exporter) collectMetrics(parentCtx context.Context) (err error) { if len(errs) > 0 { span.AddEvent("could not collect metrics") - return fmt.Errorf("could not collect metrics: %v", errs) + return fmt.Errorf("could not collect metrics: %w", combineErrors(errs)) } return nil diff --git a/core/commandline/shell.go b/core/commandline/shell.go index ef5f10b2da..ff9e5dd1a3 100644 --- a/core/commandline/shell.go +++ b/core/commandline/shell.go @@ -3,6 +3,7 @@ package commandline import ( "context" "fmt" + "k8s.io/apimachinery/pkg/util/sets" "os" "os/signal" "os/user" @@ -90,11 +91,18 @@ func GenerateShellCommand(shellCommands []*cli.Command) *cli.Command { // pruneShellCommands gets a list of commands including the shell command. func pruneShellCommands(commands []*cli.Command) (prunedCommands []*cli.Command) { // initialize shell commands + nameSet := sets.NewString() for _, command := range commands { if command.Name != shellCommandName { prunedCommands = append(prunedCommands, command) } + if !nameSet.Has(command.Name) { + fmt.Printf("Command %s already exists, skipping\n", command.Name) + } + + nameSet.Insert(command.Name) } + return prunedCommands } diff --git a/docs/bridge/CHANGELOG.md b/docs/bridge/CHANGELOG.md index c83abed722..26fd806986 100644 --- a/docs/bridge/CHANGELOG.md +++ b/docs/bridge/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/@synapsecns/bridge-docs@0.2.4...@synapsecns/bridge-docs@0.3.0) (2024-10-02) + + +### Features + +* **rfq:** relay rate limiting ([#2933](https://github.com/synapsecns/sanguine/issues/2933)) [SLT-149] ([a240292](https://github.com/synapsecns/sanguine/commit/a2402928f7e56369ce39cc6f211c3e3b3dfd404e)) + + + + + ## [0.2.4](https://github.com/synapsecns/sanguine/compare/@synapsecns/bridge-docs@0.2.3...@synapsecns/bridge-docs@0.2.4) (2024-09-09) **Note:** Version bump only for package @synapsecns/bridge-docs diff --git a/docs/bridge/docs/01-About/01-DAO.md b/docs/bridge/docs/01-About/01-DAO.md new file mode 100644 index 0000000000..1f34cedf7c --- /dev/null +++ b/docs/bridge/docs/01-About/01-DAO.md @@ -0,0 +1,19 @@ +--- +title: Synapse DAO +--- + +# Synapse DAO + +Since launch of the Synapse Protocol, a number of governance initiatives have been put forward and voted on by the Synapse DAO. + +## Governance Model + +SYN holders with more than 100,000 SYN tokens can put forward proposals to be voted on by the DAO. SYN holders can vote using SYN on multiple chains, or delegate their votes to other token holders. + +In order to be adopted by the DAO, a proposal must gather at least `50% + 1` of all votes and reach the minimum quorum of 5 million SYN tokens. + +## Governance Components + +* **[Forum](https://forum.synapseprotocol.com/)**: A Platform to post proposals for discussion before an official vote +* **[Snapshot](https://snapshot.org/#/synapseprotocol.eth)**: A decentralized governance platform for conducting official votes. +* **[Discord](https://discord.gg/synapseprotocol)**: Discussion channel for general Synapse matters \ No newline at end of file diff --git a/docs/bridge/docs/01-About/02-Brand-Assets.md b/docs/bridge/docs/01-About/02-Brand-Assets.md new file mode 100644 index 0000000000..6ea765f2ca --- /dev/null +++ b/docs/bridge/docs/01-About/02-Brand-Assets.md @@ -0,0 +1,21 @@ +# Brand Assets + +### [synapse-brand-assets.zip](/brand-assets/synapse-brand-assets.zip) + +## Logo + +| Name | Image | +|----------------------------------------------------------------------|- +| [`synapse-logo-onLight.svg`](/brand-assets/synapse-logo-onLight.svg) | ![Synapse logo](/brand-assets/synapse-logo-onLight.svg) +| [`synapse-logo-onDark.svg`](/brand-assets/synapse-logo-onDark.svg) | ![Synapse logo](/brand-assets/synapse-logo-onDark.svg) +| [`synapse-logo-black.svg`](/brand-assets/synapse-logo-black.svg) | ![Synapse logo](/brand-assets/synapse-logo-black.svg) +| [`synapse-logo-white.svg`](/brand-assets/synapse-logo-white.svg) | ![Synapse logo](/brand-assets/synapse-logo-white.svg) + +## Mark + +| Name | Image | +|--------------------------------------------------------------------|- +| [`synapse-mark.svg`](/brand-assets/synapse-mark.svg) | ![Synapse logo](/brand-assets/synapse-mark.svg) +| [`synapse-mark-white.svg`](/brand-assets/synapse-mark-white.svg) | ![Synapse logo](/brand-assets/synapse-mark-white.svg) +| [`synapse-mark-black.svg`](/brand-assets/synapse-mark-black.svg) | ![Synapse logo](/brand-assets/synapse-mark-black.svg) +| [`synapse-border-mark.svg`](/brand-assets/synapse-border-mark.svg) | ![Synapse logo](/brand-assets/synapse-border-mark.svg) diff --git a/docs/bridge/docs/01-About/03-Routes.md b/docs/bridge/docs/01-About/03-Routes.md new file mode 100644 index 0000000000..5a6f844eb6 --- /dev/null +++ b/docs/bridge/docs/01-About/03-Routes.md @@ -0,0 +1,7 @@ +import Routes from '@site/src/components/Routes' + +# Chains & Tokens + +This page contains a list of supported tokens, listed per-chain. For a given pair, use the [Synapse Bridge](https://synapseprotocol.com) to see if a route between them exists. + + diff --git a/docs/bridge/docs/01-About/index.md b/docs/bridge/docs/01-About/index.md new file mode 100644 index 0000000000..a70db958b6 --- /dev/null +++ b/docs/bridge/docs/01-About/index.md @@ -0,0 +1,75 @@ +--- +title: About +--- + +import AnimatedLogo from '@site/src/components/AnimatedLogo' +import SVGBridge from '@site/src/components/SVGBridge' +import { BridgeFlow } from '@site/src/components/BridgeFlow' +import { CCTPFlow } from '@site/src/components/CCTPFlow' +import { RFQFlow } from '@site/src/components/RFQFlow' + + + +# Use Synapse + +Synapse is an Interchain Programming Interface. Developers read and write interchain data with Synapse, which has settled $50B in transactions between 2M+ users, and generated $30M+ in fees. + +Source: [Synapse Explorer analytics](https://explorer.synapseprotocol.com). + +## Interchain Bridge + +
+ +
Synapse Bridge
+
+ +* [Overview](/docs/Bridge) +* [Bridge guide](/docs/Bridge#how-to-bridge) + + +## Developers + +Embed or build a custom Bridge application. + +* **[SDK](/docs/Bridge/SDK)** – Call Synapse Router functions from your frontend or backend application. +* **[REST API](/docs/Bridge/REST-API)** – Endpoints and example code +* **[Widget](/docs/Bridge/Widget)** – Embed a customized Synapse Bridge in your application. + +## Synapse Routers + +Synapse Router automatically determines the appropriate router for each Bridge transaction. + +* **[Synapse Router](/docs/Routers/Synapse-Router)** – Returns and executes quotes for supported interchain transactions. +* **[CCTP](/docs/Routers/CCTP)** – Native router for USDC transactions. +* **[RFQ](/docs/Routers/RFQ)** – Relayers bid for the right to provide immediate delivery. + +
+
+ +
Synapse Router – Mint and burn any token between chains
+
+
+ +
CCTP – Use Circle contracts to mint and burn native USDC
+
+
+ +
RFQ – Take Immediate delivery from a destination relayer, who receives your origin chain assets on confirmation.
+
+
+ +## Community & Support + +Connect with other developers and the Synapse team + +* **[Discord](https://discord.gg/synapseprotocol)** +* **[Twitter](https://twitter.com/SynapseProtocol)** +* **[Telegram](https://t.me/synapseprotocol)** +* **[Forum](https://forum.synapseprotocol.com/)** + +## Additional Links + +Synapse transactions can be observed confirmed via the following methods: + +* **[Synapse Bridge](https://synapseprotocol.com)** – Bridge, Swap, and Stake via Synapse's cross-chain pools. +* **[Synapse Explorer](https://explorer.synapseprotocol.com)** – Public explorer for Synapse Bridge transactions. diff --git a/docs/bridge/docs/02-Bridge/01-SDK.md b/docs/bridge/docs/02-Bridge/01-SDK.md new file mode 100644 index 0000000000..c58d7e3f51 --- /dev/null +++ b/docs/bridge/docs/02-Bridge/01-SDK.md @@ -0,0 +1,222 @@ +--- +title: Bridge SDK +--- + +# Bridge SDK + +The Synapse Bridge SDK is the easiest way to integrate cross-chain token & liquidity transfers into your application. It is fully isomorphic and can be used on both client and server sides. + +The Synapse Bridge SDK is built on top of the [Synapse Router](/docs/Routers/Synapse-Router) contract. + +### Use cases + +* Integrate your front-end application with the Synapse Bridge. +* Provide bridge liquidity. +* Perform cross-chain arbitrage. +* Integrate the Synapse Javascript SDK with your non-Javascript application. + +## Install + +:::note requires Node v16+ + +The SDK has only been fully tested on Node 16+ or greater. Earlier versions are not guaranteed to work. + +::: + +Requires either the `npx` or `yarn` package manager. + +| Options +|- +| `npx install @synapsecns/sdk-router` +| `yarn install @synapsecns/sdk-router` + +## Configure Ethers + +The SDK package relies on the `@ethersproject` and `ethers` dependencies, installed from `npm`. + +To begin constructing bridge-related transactions, first set up your environment wiht your providers, and format them, along with the `chainIds` you will be using, to set up a `SynapseSDK` instance. + +#### Ethers v5 + +```js +//Set up providers (RPCs) for each chain desired +const arbitrumProvider: Provider = new ethers.providers.JsonRpcProvider( + 'https://arb1.arbitrum.io/rpc' +) +const avalancheProvider: Provider = new ethers.providers.JsonRpcProvider( + 'https://api.avax.network/ext/bc/C/rpc' +) + +//Structure arguments properly +const chainIds = [42161, 43114] +const providers = [arbitrumProvider, avalancheProvider] + +//Set up a SynapseSDK instance +const Synapse = new SynapseSDK(chainIds, providers) +``` + +#### Ethers v6 + +:::tip Ethers v6 + +Use of Ethers v6 requires the `@ethersproject/providers` dependency to be installed via `npm` or `yarn`: +* `npm install @ethersproject/providers@^5.7.2` +* `yarn add @ethersproject/providers@^5.7.2` + +::: + +```js +import { JsonRpcProvider } from '@ethersproject/providers' + +//Set up providers (RPCs) for each chain desired +const arbitrumProvider: Provider = new JsonRpcProvider( + 'https://arb1.arbitrum.io/rpc' +) +const avalancheProvider: Provider = new JsonRpcProvider( + 'https://api.avax.network/ext/bc/C/rpc' +) + +//Structure arguments properly +const chainIds = [42161, 43114] +const providers = [arbitrumProvider, avalancheProvider] + +//Set up a SynapseSDK instance +const Synapse = new SynapseSDK(chainIds, providers) +``` + +## Functions + +:::tip Query Data Type + +`originQuery` and `destQuery`, returned by `bridgeQuote()` and required for `bridge()`, are [`Query`](https://synapserouter.gitbook.io/untitled/) objects, which contain: + +* `swapAdapter`: (string): 0x address of the swap adapter. +* `tokenOut`: (string): 0x address of the outputted token on that chain. +* `minAmountOut`: (Ethers BigNumber): The min amount of value exiting the transaction. +* `deadline`: (Ethers BigNumber): The deadline for the potential transaction. +* `rawParams`: (string): 0x params for the potential transaction. + +::: + +### `bridgeQuote()` + +Get all relevant information regarding a possible transaction. + +#### Parameters + +`bridgeQuote()` requires the following arguments: + +* `fromChain` (number): Origin chain id. +* `toChain` (number): Destination chain id. +* `fromToken` (string): 0x token address on the origin chain. +* `toToken` (string): 0x token address on the destination chain. +* `amount` (Ethers BigNumber): The amount (with the correct amount of decimals specified by the token on the origin chain) +* `object` (three seperate args): +* `deadline` (Ethers BigNumber): Deadline for the transaction to be initiated on the origin chain, in seconds (optional) +* `originUserAddress` (string): Address of the user on the origin chain, optional, mandatory if a smart contract is going to initiate the bridge operation +* `excludedModules` (array): (optional) List of bridge modules to exclude from the result + +#### Return value + +`bridgeQuote` returns the following information + +* `feeAmount` (Ethers BigNumber): The calculated amount of fee to be taken. +* `bridgeFee` (number): The percentage of fee to be taken. +* `maxAmountOut` (Ethers BigNumber): The maximum output amount resulting from the bridge transaction. +* `originQuery` (`Query`): The query to be executed on the origin chain. +* `destQuery` (`Query`): The query to be executed on the destination chain. + +### `bridge()` + +Use `bridgeQuote` to request a Bridge transaction + +#### Parameters + +* `toAddress` (number): The 0x wallet address on the destination chain. +* `routerAddress` (string): The 0x contract address on the origin chain of the bridge router contract. +* `fromChain` (number): The origin chain id. +* `toChain` (number): The destination chain id. +* `fromToken` (string): The 0x token address on the origin chain. +* `amount` (Ethers BigNumber): The amount (with the correct amount of decimals specified by the token on the origin chain) +* `originQuery` (`Query`): The query to be executed on the origin chain. +* `destQuery` (`Query`): The query to be executed on the destination chain. + +#### Return value + +* `to` (string): 0x wallet address on the destination chain. +* `data` (string): Output data in 0x hex format + +### `allBridgeQuotes()` + +#### Return value + +Returns an array all possible bridge quotes, with the first item in the array being the cheapest route. + +Quotes are returned from various Bridge "types" such as [CCTP](/docs/Routers/CCTP) or [RFQ](/docs/Routers/RFQ). More information is available on the [Synapse Router](/docs/Routers/Synapse-Router) page, or [SynapseCNS](https://github.com/synapsecns/sdk-router) Github repository. + +## Examples + +### Get a quote + +```js +const quotes = await Synapse.bridgeQuote( + routerAddress + 42161, // Origin Chain + 43114, // Destination Chain + '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', // Origin Token Address + '0xa7d7079b0fead91f3e65f86e8915cb59c1a4c664', // Destination Token Address + BigNumber.from('20000000') // Amount in + { + // Deadline for the transaction to be initiated on the origin chain, in seconds (optional) + deadline: 1234567890, + // List of bridge modules to exclude from the result, optional. + // Empty list means that all modules are included. + excludedModules: ['SynapseBridge', 'SynapseCCTP', 'SynapseRFQ'], + // Address of the user on the origin chain, optional. + // MANDATORY if a smart contract is going to initiate the bridge operation on behalf of the user. + originUserAddress: '0x1234567890abcdef1234567890abcdef12345678', + } +) +``` + +### Request a transaction + +```js +await Synapse.bridge( + '0x0AF91FA049A7e1894F480bFE5bBa20142C6c29a9', // To Address + bridgeQuote.routerAddress, // address of the contract to route the txn + 42161, // Origin Chain + 43114, // Destination Chain + '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', // Origin Token Address + BigNumber.from('20000000'), // Amount + quote.originQuery, // Origin query from bridgeQuote() + quote.destQuery // Destination query from bridgeQuote() +) +``` + +## Version 0.10.0 breaking changes + +### Options object + +* `deadline`, `excludeCCTP` (now `excludedModules`), and `originUserAddress` parameters are now found in an (optional) options object at the end of the arguments list for `bridgeQuote()`, and `allBridgeQuotes()`. +* `excludedModules` excludes one or more modules with an array of the module names. Supported names are `SynapseBridge`, `SynapseCCTP`, and `SynapseRFQ`. +* `originUserAddress` is required as part of the options object to initiate a bridge transaction on behalf of a user. + +### Examples +```js +bridgeQuote(...arguments, { + deadline: 1234567890, + excludedModules: ["SynapseCCTP"], + originUserAddress: "0x1234...", +}) + +allBridgeQuotes({ + deadline: 1234567890, + excludedModules: ["SynapseCCTP"], + originUserAddress: "0x1234...", +}) +``` + +### `FastBridgeRouter` + +The previous `FastBridgeRouter` deployment is deprecated, if your integration is using the hardcoded address, please see the router deployments/deprecated deployments table [here](https://github.com/synapsecns/sanguine/tree/master/packages/sdk-router#router-deployments) diff --git a/docs/bridge/docs/02-Bridge/02-REST-API.md b/docs/bridge/docs/02-Bridge/02-REST-API.md new file mode 100644 index 0000000000..8b46ddad60 --- /dev/null +++ b/docs/bridge/docs/02-Bridge/02-REST-API.md @@ -0,0 +1,205 @@ +--- +title: REST API +--- + +# REST API + +Get read-only data from on-chain Synapse contracts, and generate Bridge and Swap quotes, plus additional transaction information. + +## API-docs + +[`api.synapseprotocol.com/api-docs`](https://api.synapseprotocol.com/api-docs) + +## Previous versions + +| Date | Description +|------------------------|- +| 2024‑10‑01 | [https://synapse-rest-api-v2.herokuapp.com/](https://synapse-rest-api-v2.herokuapp.com/) is no longer maintained and has been fully deprecated as of October 2024. + +## Support + +Please read the documentation and examples carefully before reaching out on [Discord](https://discord.gg/synapseprotocol) for questions. + + + diff --git a/docs/bridge/docs/02-Bridge/03-Widget.md b/docs/bridge/docs/02-Bridge/03-Widget.md new file mode 100644 index 0000000000..eed13ea07e --- /dev/null +++ b/docs/bridge/docs/02-Bridge/03-Widget.md @@ -0,0 +1,196 @@ +--- +title: Widget +--- + +import SVGWidget from '@site/src/components/SVGWidget' + +# Bridge Widget + +The Synapse Widget lets you quickly and easily add bridging to their DeFi application, access specific tokens supported by the Synapse protocol, or even custom-build your own Synapse frontend. + +
+ +
Synapse Widget
+
+ +## Install + +Requires `React`, and the `npm` or `yarn` package manager. + +| Options | +|-| +| `npm install @synapsecns/widget` +| `yarn add @synapsecns/widget` + +## Quick start + +`import { Bridge } from @synapsecns/widget` into your app, and initialize it with your `web3Provider`. + +```jsx +import { Bridge } from '@synapsecns/widget' + +const MyApp = () => { + const web3Provider = new ethers.BrowserProvider(window.ethereum) + + return +} +``` + +This will result in a fully operational Bridge integrating the routes and tokens supported by the Synapse protocol. + +## Properties + +While the widget works out-of-the-box without any setup beyond `web3Provider`, configuration is recommended for reliability and performance. + +* `web3Provider` (required): Handles wallet connections. +* `customRpcs` (recommended): JSON-RPC endpoints. +* `targetChainIds`: List of destination chain IDs. Defaults to *all*. +* `targetTokens`: List of tokens to display. These tokens are imported from the widget package. Defaults to *all*. +* `customTheme`: Custom theme for the widget. Defaults to light mode. see [Theme](#theme) section for details. +* `container`: Includes a solid-background container if `true`. Defaults to `false`. +* `protocolName`: Short name by which to identify the protocol. Defaults to `"Target"`. +* `hideConsoleErrors`: Hide SDK and Widget `console.error` messages. Defaults to `false`. + +## web3Provider + +While the demo landing page uses the `ethers` library, any similar provider can be used. + +```jsx +// Ethers v5 +const web3Provider = new ethers.providers.Web3Provider(window.ethereum, 'any') + +// Ethers v6 +const web3Provider = new ethers.BrowserProvider(window.ethereum) +``` + +## customRpcs + +Set preferred RPC endpoints for each `chainId`. Defaults to Synapse fallback values for undefined chains. + +```jsx +import { Bridge, CustomRpcs } from '@synapsecns/widget' + +const customRpcs: CustomRpcs = { + 1: 'https://ethereum.my-custom-rpc.com', + 10: 'https://optimism.my-custom-rpc.com', + 42161: 'https://arbitrum.my-custom-rpc.com', +} + +const MyApp = () => { + const web3Provider = new ethers.BrowserProvider(window.ethereum) + + return +} +``` + +## targetChainIds & targetTokens + +Shows only the chains and tokens your project supports for onboarding, while still allowing users to onboard from, or bridge back to, any preferred chain or token. + +```jsx +import { Bridge, CustomRpcs, ETH, USDC, USDT } from '@synapsecns/widget' + +const MyApp = () => { + const web3Provider = new ethers.BrowserProvider(window.ethereum) + + return ( + + ) +} +``` + +:::tip Token names + +Token names must match the definitions in `src/constants/bridgeable.ts`. Metis USDC, for example, is `"METISUSDC"`. + +For chains, see `src/constants/chains.ts` as well. + +::: + +## Theme + +The widget will automatically generate a color palette from `bgColor` in the `customTheme` object, which you can also use to override individual color variables. + +:::tip Color modes + +If your application has multiple color modes, such as light and dark, reload the widget with the appropriate theme colors when your application’s theme changes. + +::: + +```jsx +const customTheme = { + // Generate from base color, 'dark', or 'light' + bgColor: '#08153a', + + // Basic customization + '--synapse-text': 'white', + '--synapse-secondary': '#ffffffb3', + '--synapse-root': '#16182e', + '--synapse-surface': 'linear-gradient(90deg, #1e223de6, #262b47e6)', + '--synapse-border': 'transparent', + + // Full customization (Uses 'basic' colors by default) + '--synapse-focus': 'var(--synapse-secondary)', + '--synapse-select-bg': 'var(--synapse-root)', + '--synapse-select-text': 'var(--synapse-text)', + '--synapse-select-border': 'var(--synapse-border)', + '--synapse-button-bg': 'var(--synapse-surface)', + '--synapse-button-text': 'var(--synapse-text)', + '--synapse-button-border': 'var(--synapse-border)', + + // Transaction progress colors (set bgColor to auto-generate) + '--synapse-progress': 'hsl(265deg 100% 65%)', + '--synapse-progress-flash': 'hsl(215deg 100% 65%)', + '--synapse-progress-success': 'hsl(120deg 100% 30%)', + '--synapse-progress-error': 'hsl(15deg 100% 65%)', +} + + +``` + +## Container + +The widget is full-width with a transparent background by default, but can supply its own solid background container. + +```jsx + +``` + +## useBridgeSelections hook + +An active list of dropdown selections are available from the `useBridgeSelections` React hook. + +```jsx +const { + originChain, + originToken, + destinationChain, + destinationToken, +} = useBridgeSelections() +``` + +### Structure + +* `Chain: { id: number, name: string }` +* `Token: { symbol: string, address: string }` + +## Example Apps + +For reference, you can find three example apps in the repository’s `/examples` folder. + +| `examples/` | Description +|-----------------|- +| `landing-page` | Functional demo with basic customization +| `with-react` | Simple React implementation +| `with-next` | Simple Next.js implementation + +## Support + +If you have questions or need help implementing the widget, reach out to the team on [Discord](https://discord.gg/synapseprotocol). diff --git a/docs/bridge/docs/02-Bridge/04-Code-Examples.md b/docs/bridge/docs/02-Bridge/04-Code-Examples.md new file mode 100644 index 0000000000..1816550110 --- /dev/null +++ b/docs/bridge/docs/02-Bridge/04-Code-Examples.md @@ -0,0 +1,350 @@ +--- +sidebar_label: Examples +--- + +# Example Code + +Example SDK & API implementations + +## Basic Implementation + +```js +// app.js + +import { JsonRpcProvider } from '@ethersproject/providers' +import { Provider } from '@ethersproject/abstract-provider' +import { SynapseSDK } from '@synapsecns/sdk-router' +import { BigNumber } from '@ethersproject/bignumber' + +//Set up providers (RPCs) for each chain desired** +const arbitrumProvider: Provider = new JsonRpcProvider('https://arb1.arbitrum.io/rpc') +const avalancheProvider: Provider = new JsonRpcProvider('https://api.avax.network/ext/bc/C/rpc') + +//Structure arguments properly +const chainIds = [42161, 43114] +const providers = [arbitrumProvider, avalancheProvider] + +//Set up a SynapseSDK instance +const Synapse = new SynapseSDK(chainIds, providers) + +// quote +const quote = await Synapse.bridgeQuote( + 42161, // From Chain + 43114, // To Chain + '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', // From Token + '0xa7d7079b0fead91f3e65f86e8915cb59c1a4c664', // To Token + BigNumber.from('20000000'), // Amount + { + deadline: 1234567890, + excludedModules: ['SynapseRFQ'], + originUserAddress: '0x1234567890abcdef1234567890abcdef12345678', + } +) + +// bridge +await Synapse.bridge( + '0x0AF91FA049A7e1894F480bFE5bBa20142C6c29a9', // To Address + quote.routerAddress, // bridge router contract address + 42161, // Origin Chain + 43114, // Destination Chain + '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', // Origin Token Address + BigNumber.from('20000000'), // Amount + quote.originQuery, // Origin query from bridgeQuote() + quote.destQuery // Destination query from bridgeQuote() +) +``` + +## NextJS Implementation + +:::note Dependencies + +Wagmi, RainbowKit + +::: + +### App + +```tsx +// app.tsx + +import '@styles/global.css' +import '@rainbow-me/rainbowkit/styles.css' +import { SynapseProvider } from '@/utils/SynapseProvider' +import type { AppProps } from 'next/app' +import { Provider as EthersProvider } from '@ethersproject/abstract-provider' +import { JsonRpcProvider } from '@ethersproject/providers' +import { configureChains, createClient, WagmiConfig } from 'wagmi' +import { + mainnet, + arbitrum, + aurora, + avalanche, + bsc, + canto, + fantom, + harmonyOne, + metis, + moonbeam, + moonriver, + optimism, + polygon, +} from 'wagmi/chains' + +import { + getDefaultWallets, + RainbowKitProvider, + darkTheme, +} from '@rainbow-me/rainbowkit' +import { alchemyProvider } from 'wagmi/providers/alchemy' +import { publicProvider } from 'wagmi/providers/public' + +export default function App({ Component, pageProps }: AppProps) { + const { chains, provider } = configureChains([mainnet, + arbitrum, + aurora, + avalanche, + bsc, + canto, + fantom, + harmonyOne, + metis, + moonbeam, + moonriver, + optimism, + polygon], [ + alchemyProvider({ apiKey: 'API_KEY' }), + publicProvider(), + ]) + + const { connectors } = getDefaultWallets({ + appName: 'ExampleApp', + chains, + }) + + const wagmiClient = createClient({ + autoConnect: true, + connectors, + provider, + }) + + // Synapse client params setup example + let synapseProviders: EthersProvider[] = [] + chains.map((chain) => { + let rpc: EthersProvider = new JsonRpcProvider(chain.rpcUrls.default.http[0]) + synapseProviders.push(rpc) + }) + + return ( + + + chain.id)} + providers={synapseProviders} + > + + + + + ) +} +``` + +### Provider + +```tsx +// `@/utils/SynapseProvider.tsx` + +import { createContext, useContext } from 'react' +import { SynapseSDK } from '@synapsecns/sdk-router' +import { Provider } from '@ethersproject/abstract-provider' + +export const SynapseContext = createContext(null) + +export const SynapseProvider = ({ + children, + chainIds, + providers, +}: { + children: React.ReactNode + chainIds: number[] + providers: Provider[] +}) => { + const sdk = new SynapseSDK(chainIds, providers) + return ( + {children} + ) +} + +export const useSynapseContext = () => useContext(SynapseContext) +``` + +### Homepage + +```tsx +// `/homepage/index.tsx` + +import { useSynapseContext } from '@/utils/SynapseProvider' +import { Zero } from '@ethersproject/constants' +import { useState, useEffect } from 'react' +import { getNetwork } from '@wagmi/core' +import { + DEFAULT_FROM_CHAIN, + DEFAULT_TO_CHAIN, + DEFAULT_FROM_TOKEN, + DEFAULT_TO_TOKEN, +} from '@/constants/bridge' + +export default function HomePage({ address }: { address: `0x${string}` }) { + // Get the synapse sdk + const SynapseSDK = useSynapseContext() + + // Get the current time + const time = // add logic to get the current unix timestamp + // Example state hooks + const [fromToken, setFromToken] = useState(DEFAULT_FROM_TOKEN) + const [toToken, setToToken] = useState(DEFAULT_TO_TOKEN) + const [fromChainId, setFromChainId] = useState(DEFAULT_FROM_CHAIN) + const [toChainId, setToChainId] = useState(DEFAULT_TO_CHAIN) + const [amount, setAmount] = useState(Zero) + const [bridgeQuote, setBridgeQuote] = useState({ + outputAmountString: '', + quotes: { originQuery: null, destQuery: null }, + }) + + // Set connected network when component is mounted + useEffect(() => { + const { chain: fromChainIdRaw } = getNetwork() + setFromChainId(fromChainIdRaw ? fromChainIdRaw?.id : DEFAULT_FROM_CHAIN) + }, []) + + // Get Quote function + // Suggestion: this function should be triggered from an useEffect when amount or to/from token/chain is altered + const getQuote = async () = { + SynapseSDK.bridgeQuote( + fromChainId, + toChainId, + fromToken, + toToken, + amount, + { + deadline: time + 10000, + excludedModules: [], + originUserAddress: address, + } + ) + .then( + ({ feeAmount, bridgeFee, maxAmountOut, originQuery, destQuery }) => { + let toValueBigNum = maxAmountOut ?? Zero + let toValueBase = toValueBigNum.div(toDecimals).toString() + let toValueMantissa = toValueBigNum.mod(toDecimals).toString() + + setBridgeQuote({ + outputAmountString: toValueBase + '.' + toValueMantissa, + quotes: { + originQuery, + destQuery, + }, + }) + // do something + } + ) + .catch((err) => { + alert('error getting quote', err) + // do something + }) + + } + + // Execute bridge function + const executeBridge = async () = { + await Synapse.bridge( + toAddress, // To Address + bridgeQuote.routerAddress, // bridge router contract address + fromChainId, // Origin Chain + toChainId, // Destination Chain + fromToken, // Origin Token Address + amount, // Amount + bridgeQuote.quotes.originQuery, // Origin query from bridgeQuote() + bridgeQuote.quotes.destQuery // Destination query from bridgeQuote() + ).then(({to, data}) => { + // do something + } + ) + .catch((err) => { + alert('error bridging', err) + // do something + }) + } + +// ... + +} +``` + +## API Functions + +### Estimate bridge output + +```js +async function estimateBridgeOutput( + fromChain, + toChain, + fromToken, + toToken, + amountFrom +) { + const query_string = `fromChain=${fromChain}&toChain=${toChain}&fromToken=${fromToken}&toToken=${toToken}&amountFrom=${amountFrom}`; + const response = await fetch( + `https://api.synapseprotocol.com/bridge?${query_string}` + ); + + const response_json = await response.json(); + // Check if the response is an array and has at least one item + if (Array.isArray(response_json) && response_json.length > 0) { + return response_json[0]; // Return the first item + } else { + throw new Error('No bridge quotes available'); + } +} + +estimateBridgeOutput( + 1, // Ethereum + 42161, // Arbitrum + "USDC", + "USDC", + "1000" +).then(firstQuote => { + console.log('First bridge quote:', firstQuote); +}).catch(error => { + console.error('Error:', error.message); +}); +``` + +### Generate unsigned bridge transaction + +```js +async function generateUnsignedBridgeTxn( + fromChain, + toChain, + fromToken, + toToken, + amountFrom, + destAddress +) { + const query_string = `fromChain=${fromChain}&toChain=${toChain}&fromToken=${fromToken}&toToken=${toToken}&amount=${amountFrom}&destAddress=${addressTo}`; + const response = await fetch( + `https://api.synapseprotocol.com/bridgeTxInfo?${query_string}` + ); + const response_json = await response.json(); + return await response_json; +} + +generateUnsignedBridgeTxn( + 1, // Ethereum + 42161, // Arbitrum + "USDC", + "USDC", + "1000" + "0x2D2c027E0d1A899a1965910Dd272bcaE1cD03c22" +); +``` diff --git a/docs/bridge/docs/02-Bridge/index.md b/docs/bridge/docs/02-Bridge/index.md new file mode 100644 index 0000000000..dfa2ba2519 --- /dev/null +++ b/docs/bridge/docs/02-Bridge/index.md @@ -0,0 +1,58 @@ +--- +title: Bridge +--- + +import { BridgeFlow } from '@site/src/components/BridgeFlow' +import SVGBridge from '@site/src/components/SVGBridge' + +# Synapse Bridge + +The [Synapse Bridge](https://synapseprotocol.com) and [Solana Bridge](https://solana.synapseprotocol.com/) seamlessly swap on-chain assets between [20+ EVM and non-EVM blockchains](/docs/About/Routes) in a safe and secure manner. + +
+ +
+ +
User assets are sent to a bridge contract, moved to the `destChain`, and returned to the user.
+
+ +## Developers + +Add the [Custom Widget](#) to your DeFi application, or build your own DeFi applications using the [Synapse SDK](#). + +## Bridge Functions + +The [Synapse Router](#) will return an appropriate bridge function based on the chains and tokens involved. + +* **Canonical** – Assets are wrapped and then bridged. +* **[Liquidity Pools](#)** – Assets are swapped via Synapse liquidity pools. +* **[CCTP](#)** – Native router for USDC + +## Pool Liquidity + +![liquidity pool tokens](lp-tokens.svg)\ +Synapse liquidity pools use the nexus USD (nUSD) and nexus ETH (nETH) interchain stablecoins. nUSD and nETH are fully backed by the nexus USD and nexus ETH liquidity pools on Ethereum. + +When a token is bridged using a Synapse Liquidity Pool, it is converted to a nexus token on the source chain, which is then bridged to the destination chain, before being converted back into a native token. + +## How to Bridge + +
+ +
Synapse Bridge
+
+ +### Instructions + +1. Connect your wallet +2. Select your origin and destination chains from the dropdown menus +3. Select your origin token from the portfolio view, or dropdown menu +4. Enter the amount you wish to send +5. If you wish to send assets to a different wallet address, enable `Show withdrawal address` from the Settings menu (optional). +5. Connect your wallet to the origin chain, if necessary +6. Click `Bridge` to send a quote to your wallet for confirmation +7. Sign and Confirm the Bridge action from your wallet + +## Bridge Contracts + +Synapse Bridge contracts are available [here](https://docs.synapseprotocol.com/synapse-bridge/contract-addresses). diff --git a/docs/bridge/docs/02-Bridge/lp-tokens.svg b/docs/bridge/docs/02-Bridge/lp-tokens.svg new file mode 100644 index 0000000000..eec827e9ba --- /dev/null +++ b/docs/bridge/docs/02-Bridge/lp-tokens.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/bridge/docs/04-Routers/01-Synapse-Router/index.md b/docs/bridge/docs/04-Routers/01-Synapse-Router/index.md new file mode 100644 index 0000000000..7cf859b217 --- /dev/null +++ b/docs/bridge/docs/04-Routers/01-Synapse-Router/index.md @@ -0,0 +1,299 @@ +import { BridgeFlow } from '@site/src/components/BridgeFlow' + +# Synapse Router + +The Synapse Router abstracts much of the complexity around liquidity based bridging and Synapse Bridge contracts into a single [`bridge()`](/docs/Bridge/SDK/#bridge) function. + + + + + +
+ +
User assets are sent to a Bridge contract, moved to the `destChain`, and returned to the user.
+
+ +## Queries + +:::note Intermediate tokens + +Some Bridge transactions utilize an [intermediary token](/docs/Bridge/#pool-liquidity) (nETH, nUSD) the protocol has mint/burn permissions over. + +Swaps to/from an intermediate token return an empty query. + +::: + +Determining an optimal route with minimal slippage and maximum output is not trivial. Synapse Router condenses this complexity into a [`Query`](https://github.com/synapsecns/synapse-contracts/blob/7bc3a579c08838d7f4c3e62954135901abb16183/contracts/bridge/libraries/BridgeStructs.sol#L12-L18), or data structure of generic swap instructions that return a `token`, and an `amountReceived`. + + + +Given a bridgable token, a `Query` identifies on-chain “swaps” which allow the Bridge to identify the route(s) for an origin and intermediate token swap on the origin chain, and subsequent token swap on the destination chain. + +Origin and destination queries are taken from the `getOriginAmountOut()` and `getDestinationAmountOut()` supporting functions, which are called for each transaction, from which your application can decide its preferred route. + + + + +## Constructing a Bridge transaction + +:::tip Avoid manual construction + +Attempting to manually construct queries may result in transactions being reverted for misconfigured parameters. Always use the Router functions below to construct your Bridge transactions. + +::: + +**1. Get output `tokenSymbols`** + +Call `getConnectedBridgeTokens()` with your output token to receive a formatted `tokenSymbols` list. + +**2. Get `originQuery` list** + +Call `getOriginAmountOut()` with your input token, `tokenSymbols` list, and `amountIn`, to receive a `Query` list for the origin chain. + +**3. Get `destRequest` list** + +Convert each origin `Query` to a `destRequest` as seen in this example: + +```js +let requests = symbols.map((value, index) => { + let request: DestRequest = { + symbol: value, + amountIn: originQueries[index].minAmountOut, + }; + return request; +}); +``` + +**4. Get `destQuery` list** + +Call `getDestinationAmoutOut()` with your `destRequest` list and output token to receive a `Query` list for the destination chain. + +**5. select `originQuery` and `destQuery`** + +Determine which `originQuery` and `destQuery` to use. This simple example selects the origin and destination pair with the highest output: + +```js +let destQuery = maxBy(destQueries, (query) => query.minAmountOut); +let selectedIndex = destQueries.indexOf(destQuery) +let originQuery = originQueries[selectedIndex] +``` + +**6. Format queries and apply user settings** + +Add any user settings such as `slippage`, and `deadline` to your queries, and specify a [`swapAdapter`](#swap-adapter) for the swap to use. + +**7. `bridge()`** + +Call `bridge()` with your selected `originQuery` and `destQuery` pair. + +## Swap Adapter + +:::note + +SynapseRouterV1's swap adapter supports Synapse hosted pools. Future versions will allow additional adapters to support aggregators on different chains, allowing any-to-any Bridge transactions. + +::: + +The Synapse Adapter is a configurable wrapper that facilitates the "swap" action on the origin and destination chains, and exposes useful methods to get `Quote` and `Query` structs, supported pools, tokens, and more. + +## Example + +### Direct contract integration + +```js +/** + * Struct representing a bridge token. + * @param symbol Bridge token symbol: unique token ID consistent among all chains + * @param token Bridge token address + */ +type BridgeToken = { + symbol: String; + token: Address; +}; + +/** + * Struct representing a request for a swap quote from a bridge token. + * @param symbol Bridge token symbol: unique token ID consistent among all chains + * @param amountIn Amount of bridge token to start with, before the bridge fee is applied + */ +type DestRequest = { + symbol: String; + amountIn: BigInt; +}; + +/** + * Struct representing a request swap (list of instructions) for SynapseRouter. + * @param swapAdapter Adapter address that will perform the swap. Address(0) specifies a "no swap" query. + * @param tokenOut Token address to swap to. + * @param minAmountOut Minimum amount of tokens to receive after the swap, or tx will be reverted. + * @param deadline Latest timestamp for when the transaction needs to be executed, or tx will be reverted. + * @param rawBytes ABI-encoded params for the swap that will be passed to `swapAdapter`. + */ +type SwapQuery = { + swapAdapter: Address; + tokenOut: Address; + minAmountOut: BigInt; + deadline: BigInt; + rawParams: BytesLike; +}; + +type UserSettings = { + maxSlippage: BigInt; + deadlineOrigin: BigInt; + deadlineDest: BigInt; +}; + +interface SynapseRouter { + /** + * Initiate a bridge transaction with an optional swap on both origin and destination chains + * @param to Address to receive tokens on destination chain + * @param chainId Destination chain id + * @param token Initial token for the bridge transaction to be pulled from the user + * @param amount Amount of the initial tokens for the bridge transaction + * @param originQuery Origin swap query. Empty struct indicates no swap is required + * @param destQuery Destination swap query. Empty struct indicates no swap is required + */ + bridge( + to: Address, + chainId: Number, + token: Address, + amount: BigInt, + originQuery: SwapQuery, + destQuery: SwapQuery + ): null; + + /** + * Gets the list of all bridge tokens (and their symbols), such that destination swap + * from a bridge token to `tokenOut` is possible. + * @param tokenOut Token address to swap to on destination chain + */ + getConnectedBridgeTokens(tokenOut: Address): BridgeToken[]; + + /** + * Finds the best path between `tokenIn` and every supported bridge token from the given list, + * treating the swap as "origin swap", without putting any restrictions on the swap. + * @param tokenIn Initial token that user wants to bridge/swap + * @param tokenSymbols List of symbols representing bridge tokens + * @param amountIn Amount of tokens user wants to bridge/swap + */ + getOriginAmountOut( + tokenIn: Address, + tokenSymbols: String[], + amountIn: BigInt + ): SwapQuery[]; + + /** + * Finds the best path between every supported bridge token from the given list and `tokenOut`, + * treating the swap as "destination swap", limiting possible actions to those available for every bridge token. + * Will take the bridge fee into account, when returning a quote for every bridge token. + * @param requests List of structs with following information: + * - symbol: unique token ID consistent among all chains + * - amountIn: amount of bridge token to start with, before the bridge fee is applied + * @param tokenOut Token user wants to receive on destination chain + */ + getDestinationAmountOut( + requests: DestRequest[], + tokenOut: Address + ): SwapQuery[]; +} + +/// Perform a cross-chain swap using Synapse:Bridge +/// Start from `amountIn` worth of `tokenIn` on origin chain +/// Receive `tokenOut` on destination chain +function synapseBridge( + originChainId: Number, + destChainId: Number, + tokenIn: Address, + tokenOut: Address, + amountIn: BigInt, + userOrigin: Address, + userDest: Address, + userSettings: UserSettings +) { + // Every cross-chain swap via Synapse:Bridge is fueled by using one of the + // supported "bridge tokens" as the intermediary token. + // A following set of actions will be initiated by a single SynapseRouter.bridge() call: + // - Origin chain: tokenIn -> bToken swap is performed + // - Synapse: bridge bToken from origin chain to destination + // - Destination chain: bToken -> tokenOut is performed + + // Here we describe a list of actions to perform such a cross-chain swap, knowing only + // - tokenIn, tokenOut, amountIn + // - SynapseRouter deployments + // - User settings for maximum slippage and deadline + // - User address on origin and destinaion chain (might be equal or different) + + // Beware: below is a TypeScript pseudocode. + + // 0. Fetch deployments of SynapseRouter on origin and destiantion chains + let routerOrigin = getSynapseRouter(originChainId); + let routerDest = getSynapseRouter(destChainId); + + // 1. Determine the set of bridge tokens that could enable "receive tokenOut on destination chain" + // For that we pefrorm a static call to SynapseRouter on destination chain + let bridgeTokens = routerDest.getConnectedBridgeTokens(tokenOut); + // Then we get the list of bridge token symbols + let symbols = bridgeTokens.map((token) => token.symbol); + + // 2. Get the list of Queries with possible swap instructions for origin chain + // For that we pefrorm a static call to SynapseRouter on origin chain + // This gets us the quotes from tokenIn to every bridge token (one quote per bridge token in the list) + let originQueries = routerOrigin.getOriginAmountOut( + tokenIn, + symbols, + amountIn + ); + + // 3. Get the list of Queries with possible swap instructions for destination chain + // First, we form a list of "destiantion requests" by merging + // list of token symbols with list of quotes obtained in step 2. + let requests = symbols.map((value, index) => { + let request: DestRequest = { + symbol: value, + amountIn: originQueries[index].minAmountOut, + }; + return request; + }); + // Then we perform a static call to SynapseRouter on destination chain + // This gets us the quotes from every bridge token to tokenOut (one quote per bridge token in the list) + // These quotes will take into account the fee for bridging the token to destination chain + let destQueries = routerDest.getDestinationAmountOut(requests, tokenOut); + + // 4. Pick a pair of originQueries[i], destQueries[i] to pefrom the cross-chain swap + // In this example we are picking the pair that yeilds the best overall quote + let destQuery = maxBy(destQueries, (query) => query.minAmountOut); + let selectedIndex = destQueries.indexOf(destQuery) + let originQuery = originQueries[selectedIndex] + + // Now we apply user slippage and deadline settings + originQuery = applyUserSettings(originQuery, userSettings) + destQuery = applyUserSettings(destQuery, userSettings) + + // 5. Call SynapseRouter on origin chain to perform a swap + let amountETH: BigInt; + // 0xEeee address is used to represent native ETH + if (tokenIn == "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE") { + // If user selected "native ETH" as tokenIn, we would need to modify msg.value for the call + amountETH = amountIn; + } else { + // If user selected an ERC-20 token as tokenIn, we would need to use msg.value=0 + amountETH = 0 + // We also need to check if user approved routerOrigin to spend `tokenIn` + if (allowance(tokenIn, userOrigin, routerOrigin) < amountIn) { + // Users needs to issue a token approval + // tokenIn.approve(routerOrigin, amountIn) + } + } + // Perform a call to Synapse Router with all the derevied parameters + // Use previously determined msg.value for this call + // (WETH wrapping is done by the Synapse Router) + routerOrigin.bridge{value: amountETH}( + userDest, + destChainId, + tokenIn, + amountIn, + originQuery, + destQuery + ); +} +``` diff --git a/docs/bridge/docs/04-Routers/CCTP/index.md b/docs/bridge/docs/04-Routers/CCTP/index.md new file mode 100644 index 0000000000..0f26283f2f --- /dev/null +++ b/docs/bridge/docs/04-Routers/CCTP/index.md @@ -0,0 +1,132 @@ +--- +sidebar_label: CCTP +--- + +import { CCTPFlow } from '@site/src/components/CCTPFlow' + +# CCTP Router + +A [Synapse Router](../Synapse-Router) bridge module which uses Circle's [Cross-Chain Transfer Protocol](https://www.circle.com/en/cross-chain-transfer-protocol) to natively mint & burn USDC. + +
+ +
User assets are sent to a Circle contract, moved to the `destChain`, and returned to the user.
+
+ +## Architecture + +### Contracts + +[Synapse CCTP contracts](/docs/Contracts/CCTP) overlay Circle CCTP contracts to mint and burn USDC and fulfill CCTP transactions. + +### Configuration +CCTP can be configured to bridge through any supported liquidity source, such as [Uniswap](https://github.com/synapsecns/synapse-contracts/blob/master/contracts/router/modules/pool/uniswap/UniswapV3Module.sol), [Curve](https://github.com/synapsecns/synapse-contracts/blob/master/contracts/router/modules/pool/curve/CurveV1Module.sol), [Algebra](https://github.com/synapsecns/synapse-contracts/blob/master/contracts/router/modules/pool/algebra/AlgebraModule.sol), [DAI PSM](https://github.com/synapsecns/synapse-contracts/blob/master/contracts/router/modules/pool/dss/DssPsmModule.sol), and others. + +### Relayer + +CCTP Relayers allow anyone to coordinate on-chain events and stored message states to send native USDC through SynapseCCTP and Circle's CCTP contracts. + +:::tip + +While the Synapse CCTP Golang relayer can be run by anyone, and is easily observable, you can also build and run your own relayer permissionlessly in any programming language. + +::: + +## Behavior + +CCTP Relayers poll for new transactions and state updates from CCTP contracts on-chain, to store in an off-chain database. + +Attestations from the [Circle API](https://developers.circle.com/stablecoin/reference) are submitted to the destination contract, and marked `Complete` when a transaction receipt is received. + +| State | Description | +|-------------|-------------| +| `Pending` | Initiated on origin chain, and pending attestation | +| `Attested` | Waiting for submission on destination chain | +| `Submitted` | Confirmed on destination chain | +| `Complete` | Completed on destination chain | + + + + +## Configure + +CCTP Relayers require a YAML configuration file path to be provided at run time. + +:::note cctp_type + +* **`synapse`** (recommended): Uses events & metadata from [Synapse CCTP contracts](/docs/Contracts/CCTP), and `synapse_cctp_address` when configuring `chains`. + +* **`circle`** (USDC to USDC only): Uses raw [TokenMessenger](https://github.com/circlefin/evm-cctp-contracts/blob/817397db0a12963accc08ff86065491577bbc0e5/src/TokenMessenger.sol) events, and `token_messenger_address` when configuring `chains`. + +::: + +### Parameters + +* `cctp_type`: Determines which event types and contracts are used. +* `chains`: `chain_id` list +* `base_omnirpc_url`: [OmniRPC service](/docs/Services/Omnirpc) base URL +* `unbonded_signer`: [Signer service](/docs/Services/Signer) — *should be a mounted secret* +* `port`: Relayer port (e.g. 8080) +* `host`: Relayer host (e.g. localhost) — *do not publicly expose*. +* `http_backoff_initial_interval_ms`: Initial backoff interval in milliseconds. +* `retry_interval_ms`: Retry interval between attestation requests in milliseconds — *[CCTP API Rate Limit](https://developers.circle.com/stablecoins/docs/limits)*. + + ### Example + +```yaml +cctp_type: "synapse" +# prod contract addresses +chains: + - chain_id: 1 + synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" + - chain_id: 42161 + synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" +base_omnirpc_url: "http://omnrpc-url/" +unbonded_signer: + type: "AWS" + # should be a mounted secret + file: "/config/aws.txt" +http_backoff_initial_interval_ms: 1000 +http_backoff_max_elapsed_time_ms: 300000 +# submitter config for cctp +submitter_config: + chains: + 1: + supports_eip_1559: true + gas_estimate: 1000000 + 42161: + gas_estimate: 30000000 + max_gas_price: 10000000000 + supports_eip_1559: true +``` + + +## Run + +### From Docker + +Run the Docker [image](https://github.com/synapsecns/sanguine/pkgs/container/sanguine%2Fcctp-relayer) along with the path to your [YAML configuration file](#configure). + +1. `docker run ghcr.io/synapsecns/sanguine/cctp-relayer:latest --config /path/to/config.yaml` + +### From Source + +:::note Requires Go 1.21 or higher + +Not generally recommended for end-users. + +::: + +Clone the Sanguine repository, then run the main.go file along with the path to your [YAML configuration file](#configure). + +1. `git clone https://github.com/synapsecns/sanguine --recursive` +2. `cd sanguine/services/cctp-relayer` +3. `go run main.go --config /path/to/config.yaml` + +### With Helm + +There is a helm chart available for the CCTP Relayer [here](https://artifacthub.io/packages/helm/synapse/cctp/0.2.0), but it is recommended you create your own. + +### Recommended services + +CCTP Relayer uses open telemetry for tracing and metrics. See the [Observability](/docs/Services/Observability) page for details. We highly recommend setting up the [Submitter Dashboard](/docs/Services/Submitter) as well. diff --git a/docs/bridge/docs/04-Routers/RFQ/01-Relayer.md b/docs/bridge/docs/04-Routers/RFQ/01-Relayer.md new file mode 100644 index 0000000000..e2f87823b5 --- /dev/null +++ b/docs/bridge/docs/04-Routers/RFQ/01-Relayer.md @@ -0,0 +1,311 @@ +--- +sidebar_position: 0 +sidebar_label: Relayer +--- + +# RFQ Relayer + +:::note + +Relayers must be whitelisted in order to fulfill bridge requests. + +::: + +A Go application which coordinates on-chain events and stored message states to relay user funds. Relayers are easily observable. + +The canonical RFQ Relayer handles **three event loops**: quoting routes, approving relays, and rebalancing funds. + +## Quote + +Continuously track and update route quotes based on changes to available and in-flight balances via [API](API). The quote should update each time the available balance or other parameters change. + +| Param | Description +|---------|- +| Balance | Maximum amount the relayer can fill +| Offset | Token price percentage, used to ensure the relayer is profitable. +| Fee | `fixed_fee_multiplier` (from [config](#configure)) multiplied by `origin_gas_estimate + destination_gas_estimate` + + + +## Relay + +Listen to on-chain events and database updates to move [`BridgeRequest`](https://vercel-rfq-docs.vercel.app/contracts/interfaces/IFastBridge.sol/interface.IFastBridge.html#bridgeparams) objects through the following states: + +| State | Description +|----------------------|- +| `Seen` | [`BridgeRequested`](https://vercel-rfq-docs.vercel.app/contracts/interfaces/IFastBridge.sol/interface.IFastBridge.html#bridgerequested) event stored in the db. +| `WillNotProcess` | [`BridgeRequested`](https://vercel-rfq-docs.vercel.app/contracts/interfaces/IFastBridge.sol/interface.IFastBridge.html#bridgerequested) event is invalid +| `NotEnoughInventory` | Retry later +| `CommittedPending` | All checks pass, waiting for transaction to finalize +| `CommittedConfirmed` | Transaction is finalized on-chain +| `RelayPending` | Called [`relay`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#relay) +| `RelayComplete` | Relay completed on-chain +| `ProvePosting` | Called [`prove`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#prove) +| `ClaimPending` | Called [`claim`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#claim) +| `ClaimComplete` | Dispute period expired, funds received. + +## Rebalance + +For cases where flows are mono-directional, the Relayer provides an interface for rebalancing funds. + +Automated rebalancing is configurable by token and currently supports CCTP routes as well as native bridging on Scroll. Rebalancing evaluation follows this logic every `rebalance_interval`: + +For each supported rebalance method, calculate the `total_balance` of each token across the chains that support that rebalance method, and which tokens have a balance below `total_balance * maintenance_balance_pct`. + +:::note + +To limit in-flight inventory, only one rebalance can be pending at a time for each token. We select the 'best' rebalance candidate as the rebalance with the largest delta between origin and destination balance. + +::: + +The rebalance amount is the smaller of the maximum rebalance amount on origin, and minimum rebalance amount on destination, clamped to the `min` and `max` values in the configuration file. + +#### Example + +| # | CCTP | USDC | `maintenence_pct` | `initial_pct` +|---|------|------|-------------------|- +| 1 | Yes | 20% | 40% | 100 USDC +| 2 | Yes | 20% | 40% | 900 USDC +| 3 | No | 20% | 40% | 2000 USDC + +The total balance of CCTP-supported chains is `1000 USDC`. Since chain 1, with 10% of the total inventory, is below the 20% maintenance threshold, the system triggers a rebalance from Chain 2 to Chain 1. + +Chain 3 does not support CCTP, and is not considered for rebalance. + +The maximum rebalance amount is `600 USDC` which takes chain 2 to its 40% initial threshold + +`300 USDC` is sent from chain 2 to chain 1 is, which is the minimum required to reach chain 1's initial 40% threshold. + +:::note Scroll + +Rebalancing for certain native bridges (e.g Scroll) is supported. It works slightly differently as flows are only supported between Scroll and Mainnet. + +At a high level, the rebalancer checks inventory on Scroll versus other chains, and if imbalanced, initiates a bridge to mainnet, allowing the CCTP relayer to rebalance funds where needed. + +::: + +## Configure + +RFQ Relayer requires a YAML configuration file path to be provided at run time. + + + +:::note rebalance_method + +`synapse` collects a fee, but does not spend user gas. Use `synapse_cctp_address` when configuring `chains`. + +`cctp` has no fees, but spends user gas. Use `token_messenger_address` when configuring `chains`. + +::: + +* `submitter_config`: Covered [here](/docs/Services/Submitter#observability). Controls gas parameters for on-chain transactions. +* `database`: Database settings for API backend. Required to store quotes and other information. SQLite with `DSN` set to a `/tmp/` directory is recommended for development. + * `type`: Database driver to use, can be `mysql` or `sqlite`. + * `dsn`: 'Data Source Name'. If using SQLite, this can be a path. For MySQL see [here](https://dev.mysql.com/doc/connector-odbc/en/connector-odbc-configuration.html) for more information. +* `screener_api_url` (optional): [Screener API](https://github.com/synapsecns/sanguine/tree/master/contrib/screener-api#screening-api) +* `rfq_url`: [Mainnet & Testnet addresses](API/#api-urls) +* `omnirpc_url`: [Running an OmniRPC instance](/docs/Services/Omnirpc) +* `rebalance_interval`: How often to rebalance. Can use `s` (seconds), `m` (minutes), or `h` (hours) +* `relayer_api_port`: Controls the relayer. Should be private or secured. +* `base_chain_config`: Default chain configuration. Overridden by individual chain configurations. See `chains` for details. +* `enable_guard`: Run a guard in the same instance. +* `submit_single_quotes`: Whether to use the batch endpoint to post quotes to the API. Useful for debugging. +* `chains`: Individual configurations to override `base_chain_config`. + * `rfq_address`: [RFQ contract address](/docs/Contracts/RFQ) for this chain. + * `confirmations`: how many confirmations to wait before acting on an event. This will vary per-chain. + * `tokens`: this is a map of token symbol→token info for this chain. For example, token may be USDC, ETH, etc + * `address`: address of the token on this chain id + * `decimals`: number of decimals this token uses. Please verify this against the token contract itself. + * `min_quote_amount`: smallest amount to quote for a given chain. This should be balanced against expected gas spend for a relayer to be profitable. `min_quote_amount` is to be given in decimal units (so 1000.00 is 1000) + * `rebalance_method`: Some tokens may not have a rebalance method. Uses `synapse` or `cctp`. + * `maintenance_balance_pct`: Portion of liquidity to maintain for this token on this chain. A balance under this amount triggers a rebalance. + * `initial_balance_pct`: Portion of liquidity to maintain after a rebalance. Should total 100% across all chains. + * `min_rebalance_amount`: Minimum amount of this token to try to rebalance. + * `max_rebalance_amount`: Maximum amount of this token to rebalance at one time. + + * `synapse_cctp_address` (optional): Used with `rebalance_method: synapse`. Uses a [Synapse CCTP address](/docs/Contracts/CCTP). + * `token_messenger_address` (optional): Used with `rebalance_method: cctp`. Tells the relayer to use the token messenger instead of Synapse. +* `quotable_tokens`: list of `[chain-id]-[token_address]: [chain-id]-[token_address]`. For example 1-0x00… could be paired with 10-0x01 + ```yaml + "1-0x00": + - "1-0x01" + ``` +* `cctp_relayer_config`: See [CCTP](/docs/Routers/CCTP). + +### Example + +
+ `config.yml` + ```yaml +submitter_config: # please see the more detailed submitter documentation + chains: + 1: + supports_eip_1559: true + gas_estimate: 1000000 +database: + type: sqlite # can be other mysql or sqlite + dsn: /tmp/db # should be the dsn of your database. If using sqlite, this can be a path + +signer: # please see more detailed signer config #can be text, gcp, or aws + type: GCP + file: /config/signer.txt + +screener_api_url: 'http://screener-url' # can be left blank +rfq_url: 'http://rfq-api' # url of the rfq api backend. +omnirpc_url: 'http://omnirpc' # url of the omnirpc instance, please reference the Omnirpc section under Services for proper configuration +rebalance_interval: 2m # how often to rebalance +relayer_api_port: '8081' # api port for the relayer api +volume_limit: 10000 # USD price cap for a bridge under block confirmation minimum (configurable under `chains`) + +base_chain_config: # this is hte base chain config, other chains override it + confirmations: 0 + # Claim (72.5k) + Prove (57.5k) gas limits, rounded up + origin_gas_estimate: 130_000 + # Relay gas limit, rounded up + dest_gas_estimate: 110_000 + quote_offset_bps: 2 + native_token: ETH + quote_pct: 90 + min_gas_token: 1000000000000000000 + fixed_fee_multiplier: 1.25 + +chains: + 1: + rfq_address: "0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E" # rfq contract address on eth + synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" # ccctp contract address on eth + token_messenger_address: "0xbd3fa81b58ba92a82136038b25adec7066af3155" # token messenger address on eth, note: only one of token_messenger_address or synapse_cctp_address actually needs to be present + cctp_start_block: 19341000 + confirmations: 2 + tokens: + USDC: + address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" + decimals: 6 + price_usd: 1.0 + min_quote_amount: 10000 + rebalance_method: "circlecctp" + maintenance_balance_pct: 20 + initial_balance_pct: 50 + max_rebalance_amount: 500000 + ETH: + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" + decimals: 18 + price_usd: 2600 + 10: + rfq_address: "0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E" + synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" + token_messenger_address: "0x2B4069517957735bE00ceE0fadAE88a26365528f" + cctp_start_block: 116855000 + l1_fee_chain_id: 1 + # Prove + Claim L1 gas estimate + l1_fee_origin_gas_estimate: 20 + # Relay L1 gas estimate + l1_fee_dest_gas_estimate: 10 + tokens: + USDC: + address: "0x0b2c639c533813f4aa9d7837caf62653d097ff85" + decimals: 6 + price_usd: 1.0 + min_quote_amount: 10000 + rebalance_method: "circlecctp" + maintenance_balance_pct: 20 + initial_balance_pct: 50 + max_rebalance_amount: 500000 + ETH: + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" + decimals: 18 + price_usd: 2600 + +quotable_tokens: + 10-0x0b2c639c533813f4aa9d7837caf62653d097ff85: + - "1-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" + 1-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48: + - "10-0x0b2c639c533813f4aa9d7837caf62653d097ff85" + 1-0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE: + - "10-0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" + 10-0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE: + - "1-0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" + +fee_pricer: + gas_price_cache_ttl: 60 + token_price_cache_ttl: 60 + +cctp_relayer_config: + cctp_type: "circle" + circle_api_url: "https://iris-api.circle.com/v1/attestations" + chains: + - chain_id: 1 + synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" + token_messenger_address: "0xbd3fa81b58ba92a82136038b25adec7066af3155" + - chain_id: 10 + synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" + token_messenger_address: "0x2B4069517957735bE00ceE0fadAE88a26365528f" + base_omnirpc_url: "http://omnirpc" # Make sure this is configured properly + unbonded_signer: + type: GCP + file: /config/signer.txt + http_backoff_initial_interval_ms: 1000 + http_backoff_max_elapsed_time_ms: 300000 + ``` +
+ +## Run + +### From Docker + +Run the Docker [image](https://github.com/synapsecns/sanguine/pkgs/container/sanguine%2Frfq-relayer) along with the path to your [YAML configuration file](#configure). + +1. `docker run ghcr.io/synapsecns/sanguine/rfq-relayer:latest --config /path/to/config` + +### From Source + +:::note Requires Go 1.21 or higher + +Not generally recommended for end-users. + +::: + +Clone the Sanguine repository, then run the main.go file along with the path to your [YAML configuration file](#configure). + +1. `git clone https://github.com/synapsecns/sanguine --recursive` +2. `cd sanguine/services/rfq/relayer` +3. `go run main.go --config /path/to/config.yaml` + +## sendChainGas + +Boolean flag available to Bridge users. When `sendChainGas` is `true`, the amount to send is specified as `chainGasAmount` in the FastBridge contract on the destination chain. + +:::note + +`chainGasAmount` is currently set to `0` on all contracts. You can ignore `sendChainGas` by only providing quotes where `sendChainGas` is not set, or `chainGasAmount` is `0`. + +::: + +## Withdrawals + +The `POST /withdraw` endpoint is exposed to allow withdrawals from the relayer wallet without needing to deal with the private key directly. This can be used for manual rebalancing. To use this feature, set the following config values: + +```yaml +enable_api_withdrawals: true +withdrawal_whitelist: + - +``` + +The relayer CLI (at `services/rfq/relayer/main.go`) exposes a withdrawal command for convenience: + +```bash +go run main.go withdraw --relayer-url https://localhost:8081 --chain-id 1 --amount 1000000000000000000 --token-address 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE --to 0x0000000000000000000000000000000000000000 +``` + +Be sure to sub in your respective `to` address! + +## Observability + +The RFQ relayer implements open telemetry for both tracing and metrics. Please see the [Observability](/docs/Services/Observability) page for more info. There is also a custom [grafana dashboard](https://github.com/synapsecns/sanguine/tree/master/services/rfq/relayer/dashboards/dashboard.json) available for the relayer. We'd also highly recommend setting up the [submitter dashboard](/docs/Services/Submitter) as well. + +![Relayer Grafana Dashboard](dashboard.png) + +The metrics exposed by the relayer are: + +- `inventory_balance`: The balance of the inventory on the chain for a given `token_name` and `relayer`. +- `quote_amount`: The amount quoted for a given `token_name` and `relayer`. +- `status_count`: The distribution of non-terminal `QuoteRequestStatus` values over time. diff --git a/docs/bridge/docs/rfq/API/get-contract-addresses.api.mdx b/docs/bridge/docs/04-Routers/RFQ/API/get-contract-addresses.api.mdx similarity index 100% rename from docs/bridge/docs/rfq/API/get-contract-addresses.api.mdx rename to docs/bridge/docs/04-Routers/RFQ/API/get-contract-addresses.api.mdx diff --git a/docs/bridge/docs/rfq/API/get-quotes.api.mdx b/docs/bridge/docs/04-Routers/RFQ/API/get-quotes.api.mdx similarity index 100% rename from docs/bridge/docs/rfq/API/get-quotes.api.mdx rename to docs/bridge/docs/04-Routers/RFQ/API/get-quotes.api.mdx diff --git a/docs/bridge/docs/rfq/API/API.md b/docs/bridge/docs/04-Routers/RFQ/API/index.md similarity index 98% rename from docs/bridge/docs/rfq/API/API.md rename to docs/bridge/docs/04-Routers/RFQ/API/index.md index dfbc77ffe3..6946022d02 100644 --- a/docs/bridge/docs/rfq/API/API.md +++ b/docs/bridge/docs/04-Routers/RFQ/API/index.md @@ -3,6 +3,8 @@ sidebar_position: 0 sidebar_label: API --- +# RFQ API + :::note This guide is mostly meant for developers who are working on building their own quoter or frontend for rfq. If you are just looking to run a relayer, please see [Relayer](../Relayer). If you are looking to integrate rfq, please see the API docs below the dropdown. @@ -48,8 +50,8 @@ The RFQ API expects the signatures to have V values as 0/1 rather than 27/28. Th ### API Urls - - mainnet: rfq-api.omnirpc.io - - testnet: rfq-api-testnet.omnirpc.io + - Mainnet: `rfq-api.omnirpc.io` + - Testnet: `rfq-api-testnet.omnirpc.io` ## Running the API: @@ -72,7 +74,7 @@ Yaml settings: - `database` - The database settings for the API backend. A database is required to store quotes and other information. Using SQLite with a dsn set to a `/tmp/` directory is recommended for development. - `type` - the database driver to use, can be `mysql` or `sqlite`. - `dsn` - the dsn of your database. If using sqlite, this can be a path, if using mysql please see [here](https://dev.mysql.com/doc/connector-odbc/en/connector-odbc-configuration.html) for more information. - - `omnirpc_url` - The omnirpc url to use for querying chain data (no trailing slash). For more information on omnirpc, see [here](../../Services/Omnirpc.md). + - `omnirpc_url` - The omnirpc url to use for querying chain data (no trailing slash). For more information on omnirpc, see [here](/docs/Services/Omnirpc). - `bridges` - A key value map of chain id to FastBridge contract address. The API will only allow quotes to be posted on these chains. - `port` - The port to run the http server on. diff --git a/docs/bridge/docs/rfq/API/relay-ack.api.mdx b/docs/bridge/docs/04-Routers/RFQ/API/relay-ack.api.mdx similarity index 100% rename from docs/bridge/docs/rfq/API/relay-ack.api.mdx rename to docs/bridge/docs/04-Routers/RFQ/API/relay-ack.api.mdx diff --git a/docs/bridge/docs/rfq/API/upsert-quote.api.mdx b/docs/bridge/docs/04-Routers/RFQ/API/upsert-quote.api.mdx similarity index 100% rename from docs/bridge/docs/rfq/API/upsert-quote.api.mdx rename to docs/bridge/docs/04-Routers/RFQ/API/upsert-quote.api.mdx diff --git a/docs/bridge/docs/rfq/API/upsert-quotes.api.mdx b/docs/bridge/docs/04-Routers/RFQ/API/upsert-quotes.api.mdx similarity index 100% rename from docs/bridge/docs/rfq/API/upsert-quotes.api.mdx rename to docs/bridge/docs/04-Routers/RFQ/API/upsert-quotes.api.mdx diff --git a/docs/bridge/docs/rfq/Relayer/dashboard.png b/docs/bridge/docs/04-Routers/RFQ/dashboard.png similarity index 100% rename from docs/bridge/docs/rfq/Relayer/dashboard.png rename to docs/bridge/docs/04-Routers/RFQ/dashboard.png diff --git a/docs/bridge/docs/04-Routers/RFQ/index.md b/docs/bridge/docs/04-Routers/RFQ/index.md new file mode 100644 index 0000000000..b44418cde1 --- /dev/null +++ b/docs/bridge/docs/04-Routers/RFQ/index.md @@ -0,0 +1,77 @@ +--- +sidebar_label: RFQ +--- + +import { RFQFlow } from '@site/src/components/RFQFlow' + +# RFQ Router + +A [Synapse Router](../Synapse-Router) bridge module which matches on-chain user requests against bridge quotes posted by decentralized [Relayers](Relayer). + +
+ +
User assets are sent to a Bridge contract, and held until a Solver executes their quote on the `destChain`.
+
+ +## Architecture + +[Synapse Fast Bridge contracts](/docs/Contracts/RFQ) coordinate decentralized Solvers to match user requests against the best quote for a given route, and secure user funds while their transaction is fulfilled. + + + +| Agents | Description +|---------|- +| Quoters | Quote distribution services run through traditional [APIs](API), or protocols like libp2p, irc, or dht. +| Solvers | Posts, then fulfills, route quotes through a [Relayer](Relayer), when matched by the Fast Bridge contract against a user request. +| Users | Uses a route quote to form a bridge request which is matched on-chain by the solver who posted the quote. +| Guards | Raises a dispute if errors or fraudulent activity are detected. + +## Behavior + +After receiving a [`BridgeRequest`](https://vercel-rfq-docs.vercel.app/contracts/interfaces/IFastBridge.sol/interface.IFastBridge.html#bridgeparams) (broadcast as a [`BridgeRequested`](https://vercel-rfq-docs.vercel.app/contracts/interfaces/IFastBridge.sol/interface.IFastBridge.html#bridgerequested) event), a Solver executes the transaction by calling [`relay`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#relay) on the Bridge contract. + +The Bridge relays the requested funds ([`msg.value`](https://ethereum.stackexchange.com/questions/43362/what-is-msg-value) in the case of ETH) from Solver to User, allowing the Solver that accepted the bridge to call [`prove`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#prove) on the Bridge contract, and receive their funds at the end of the optimistic period + +| `#` | State | Description +|-----|-------------|- +| `0` | `Null` | Bridge transaction does not exist yet on origin chain +| `1` | `Requested` | [`BridgeRequested`](https://vercel-rfq-docs.vercel.app/contracts/interfaces/IFastBridge.sol/interface.IFastBridge.html#bridgerequested) event broadcast. Waiting for Relayer +| `2` | `Proved` | Relayer called [`relay`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#relay), and [`prove`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#prove), and is waiting for the optimistic period to end. +| `3` | `Claimed` | Relayer called [`claim`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#claim) and received their funds. +| `4` | `Refunded` | Relayer did not claim within the optimistic period, or a dispute was decided in favor of the user. + + + + + + + + + + + + + + + + + +## Dispute Period and Guards + +The RFQ system includes an optimistic dispute window in which Guard contracts may initiate a dispute if they detect errors or fraudulent activity, such as incorrect fill amounts or proofs submitted by the wrong relayer. + +In a successful dispute, the relayer loses their claimable funds. This design is intended to enforce honest behavior while also protecting honest relayers in cases of blockchain reorgs. + +## Unfulfilled requests + +If a request is not fulfilled, users can reclaim their funds by using the [`claim`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#claim) function once the optimistic window has passed. diff --git a/docs/bridge/docs/05-Contracts/01-Synapse-Token.md b/docs/bridge/docs/05-Contracts/01-Synapse-Token.md new file mode 100644 index 0000000000..e839a6f865 --- /dev/null +++ b/docs/bridge/docs/05-Contracts/01-Synapse-Token.md @@ -0,0 +1,33 @@ +--- +title: Synapse Token +--- + +:::note This list may be incomplete + +The canonical list is hosted within the SynapseCNS on [Github](https://github.com/synapsecns/synapse-contracts). + +::: + +# Synapse Token + +| Chain | Address | +|-----------|----------------------------------------------| +| Arbitrum | `0x080f6aed32fc474dd5717105dba5ea57268f46eb` [↗](https://arbiscan.io/token/0x080f6aed32fc474dd5717105dba5ea57268f46eb)| +| Aurora | `0xd80d8688b02B3FD3afb81cDb124F188BB5aD0445` [↗](https://explorer.mainnet.aurora.dev/address/0xd80d8688b02B3FD3afb81cDb124F188BB5aD0445/transactions)| +| Avalanche | `0x1f1E7c893855525b303f99bDF5c3c05Be09ca251` [↗](https://snowtrace.io/address/0x1f1E7c893855525b303f99bDF5c3c05Be09ca251)| +| Base | `0x432036208d2717394d2614d6697c46DF3Ed69540` [↗](https://basescan.org/address/0x432036208d2717394d2614d6697c46DF3Ed69540)| +| Blast | `0x9592f08387134e218327E6E8423400eb845EdE0E` [↗](https://blastscan.io/address/0x9592f08387134e218327E6E8423400eb845EdE0E)| +| Boba | `0xb554A55358fF0382Fb21F0a478C3546d1106Be8c` [↗](https://blockexplorer.boba.network/tokens/0xb554A55358fF0382Fb21F0a478C3546d1106Be8c/token-transfers)| +| BSC | `0xa4080f1778e69467e905b8d6f72f6e441f9e9484` [↗](https://bscscan.com/token/0xa4080f1778e69467e905b8d6f72f6e441f9e9484)| +| Canto | `0x555982d2E211745b96736665e19D9308B615F78e` [↗](https://canto.dex.guru/address/0x555982d2e211745b96736665e19d9308b615f78e)| +| Cronos | `0xFD0F80899983b8D46152aa1717D76cba71a31616` [↗](https://cronoscan.com/address/0xFD0F80899983b8D46152aa1717D76cba71a31616)| +| DFK | `0xB6b5C854a8f71939556d4f3a2e5829F7FcC1bf2A` [↗](https://subnets.avax.network/defi-kingdoms/dfk-chain/explorer/address/0xB6b5C854a8f71939556d4f3a2e5829F7FcC1bf2A)| +| Dogechain | `0xDfA53EeBA61D69E1D2b56b36d78449368F0265c1` [↗](https://explorer.dogechain.dog/address/0xDfA53EeBA61D69E1D2b56b36d78449368F0265c1)| +| Ethereum | `0x0f2D719407FdBeFF09D87557AbB7232601FD9F29` [↗](https://etherscan.io/token/0x0f2D719407FdBeFF09D87557AbB7232601FD9F29)| +| Fantom | `0xE55e19Fb4F2D85af758950957714292DAC1e25B2` [↗](https://ftmscan.com/address/0xe55e19fb4f2d85af758950957714292dac1e25b2)| +| Harmony | `0xE55e19Fb4F2D85af758950957714292DAC1e25B2` [↗](https://explorer.harmony.one/address/0xe55e19fb4f2d85af758950957714292dac1e25b2)| +| Moonbeam | `0xF44938b0125A6662f9536281aD2CD6c499F22004` [↗](https://moonscan.io/address/0xF44938b0125A6662f9536281aD2CD6c499F22004)| +| Moonriver | `0xd80d8688b02B3FD3afb81cDb124F188BB5aD0445` [↗](https://moonriver.moonscan.io/address/0xd80d8688b02B3FD3afb81cDb124F188BB5aD0445)| +| Optimism | `0x5A5fFf6F753d7C11A56A52FE47a177a87e431655` [↗](https://optimistic.etherscan.io/address/0x5A5fFf6F753d7C11A56A52FE47a177a87e431655)| +| Polygon | `0xf8f9efc0db77d8881500bb06ff5d6abc3070e695` [↗](https://polygonscan.com/token/0xf8f9efc0db77d8881500bb06ff5d6abc3070e695)| +| Metis | `0x67c10c397dd0ba417329543c1a40eb48aaa7cd00` [↗](https://andromeda-explorer.metis.io/address/0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00)| \ No newline at end of file diff --git a/docs/bridge/docs/05-Contracts/02-Synapse-Router.md b/docs/bridge/docs/05-Contracts/02-Synapse-Router.md new file mode 100644 index 0000000000..2b0a9bc21e --- /dev/null +++ b/docs/bridge/docs/05-Contracts/02-Synapse-Router.md @@ -0,0 +1,44 @@ +--- +title: Synapse Router +--- + +:::note This list may be incomplete + +The canonical list is hosted within the SynapseCNS on [Github](https://github.com/synapsecns/synapse-contracts). + +::: + +# Synapse Router + +Synapse Router contracts route through the [SynapseBridge](https://github.com/synapsecns/synapse-contracts/blob/ed93453430635e2d43704d5599d3318c43a23033/contracts/bridge/SynapseBridge.sol#L63-L118) contract, which is used for event indexing. + +:::tip Events + +Contracts in the `deployments` folder of each chain's `SynapseBridge.json` file emit `TokenMint` or `TokenWithdraw` events when a transaction completes on the destination chain. + +::: + +**Router address**: `0x7E7A0e201FD38d3ADAA9523Da6C109a07118C96a` + +| Chain | Address | +|-----------|----------------------------------------------| +| Arbitrum | `0x6F4e8eBa4D337f874Ab57478AcC2Cb5BACdc19c9` [↗](https://arbiscan.io/address/0x6F4e8eBa4D337f874Ab57478AcC2Cb5BACdc19c9) | +| Aurora | `0xaeD5b25BE1c3163c907a471082640450F928DDFE` [↗](https://explorer.mainnet.aurora.dev/address/0xaeD5b25BE1c3163c907a471082640450F928DDFE/transactions) | +| Avalanche | `0xC05e61d0E7a63D27546389B7aD62FdFf5A91aACE` [↗](https://snowtrace.io/address/0xC05e61d0E7a63D27546389B7aD62FdFf5A91aACE) | +| Base | `0xf07d1C752fAb503E47FEF309bf14fbDD3E867089` [↗](https://basescan.org/address/0xf07d1C752fAb503E47FEF309bf14fbDD3E867089) | +| Blast | `0x55769baf6ec39b3bf4aae948eb890ea33307ef3c` [↗](https://blastscan.io/address/0x55769baf6ec39b3bf4aae948eb890ea33307ef3c) | +| Boba | `0x432036208d2717394d2614d6697c46DF3Ed69540` [↗](https://blockexplorer.boba.network/address/0x432036208d2717394d2614d6697c46DF3Ed69540/transactions) | +| BSC | `0xd123f70AE324d34A9E76b67a27bf77593bA8749f` [↗](https://bscscan.com/address/0xd123f70AE324d34A9E76b67a27bf77593bA8749f) | +| Canto | `0xDde5BEC4815E1CeCf336fb973Ca578e8D83606E0` [↗](https://evm.explorer.canto.io/address/0xDde5BEC4815E1CeCf336fb973Ca578e8D83606E0) | +| Cronos | `0xE27BFf97CE92C3e1Ff7AA9f86781FDd6D48F5eE9` [↗](https://cronoscan.com/address/0xE27BFf97CE92C3e1Ff7AA9f86781FDd6D48F5eE9) | +| DFK | `0xE05c976d3f045D0E6E7A6f61083d98A15603cF6A` [↗](https://subnets.avax.network/defi-kingdoms/dfk-chain/explorer/address/0xE05c976d3f045D0E6E7A6f61083d98A15603cF6A) | +| Dogechain | `0x9508BF380c1e6f751D97604732eF1Bae6673f299` [↗](https://explorer.dogechain.dog/address/0x9508BF380c1e6f751D97604732eF1Bae6673f299) | +| Ethereum | `0x2796317b0fF8538F253012862c06787Adfb8cEb6` [↗](https://etherscan.io/address/0x2796317b0fF8538F253012862c06787Adfb8cEb6) | +| Fantom | `0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b` [↗](https://ftmscan.com/address/0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b) | +| Harmony | `0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b` [↗](https://explorer.harmony.one/address/0xaf41a65f786339e7911f4acdad6bd49426f2dc6b) | +| Klaytn | `0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b` [↗](https://scope.klaytn.com/account/0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b?tabId=txList) | +| Metis | `0x06Fea8513FF03a0d3f61324da709D4cf06F42A5c` [↗](https://andromeda-explorer.metis.io/address/0x06Fea8513FF03a0d3f61324da709D4cf06F42A5c) | +| Moonbeam | `0x84A420459cd31C3c34583F67E0f0fB191067D32f` [↗](https://moonscan.io/address/0x84A420459cd31C3c34583F67E0f0fB191067D32f) | +| Moonriver | `0xaeD5b25BE1c3163c907a471082640450F928DDFE` [↗](https://moonriver.moonscan.io/address/0xaeD5b25BE1c3163c907a471082640450F928DDFE) | +| Optimism | `0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b` [↗](https://optimistic.etherscan.io/address/0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b) | +| Polygon | `0x8F5BBB2BB8c2Ee94639E55d5F41de9b4839C1280` [↗](https://polygonscan.com/address/0x8F5BBB2BB8c2Ee94639E55d5F41de9b4839C1280) | diff --git a/docs/bridge/docs/05-Contracts/03-Intermediate-Tokens.md b/docs/bridge/docs/05-Contracts/03-Intermediate-Tokens.md new file mode 100644 index 0000000000..5b493cf9ca --- /dev/null +++ b/docs/bridge/docs/05-Contracts/03-Intermediate-Tokens.md @@ -0,0 +1,41 @@ +--- +title: Intermediate Tokens +--- + +:::note This list may be incomplete + +The canonical list is hosted within the SynapseCNS on [Github](https://github.com/synapsecns/synapse-contracts). + +::: + +# Intermediate Tokens + +## nETH + +| Chain | Address | +|-----------|----------------------------------------------| +| Arbitrum | `0x3ea9B0ab55F34Fb188824Ee288CeaEfC63cf908e` [↗](https://arbiscan.io/address/0x3ea9B0ab55F34Fb188824Ee288CeaEfC63cf908e) | +| Base | `0xb554A55358fF0382Fb21F0a478C3546d1106Be8c` [↗](https://basescan.org/address/0xb554A55358fF0382Fb21F0a478C3546d1106Be8c) | +| Blast | `0xce971282faac9fabcf121944956da7142cccc855` [↗](https://blastscan.io/token/0xce971282faac9fabcf121944956da7142cccc855) | +| Boba | `0x96419929d7949D6A801A6909c145C8EEf6A40431` [↗](https://blockexplorer.boba.network/address/0x96419929d7949D6A801A6909c145C8EEf6A40431/transactions) | +| Optimism | `0x809DC529f07651bD43A172e8dB6f4a7a0d771036` [↗](https://optimistic.etherscan.io/address/0x809DC529f07651bD43A172e8dB6f4a7a0d771036) | +| Metis | `0x931b8f17764362a3325d30681009f0edd6211231` [↗](https://andromeda-explorer.metis.io/address/0x931B8f17764362A3325D30681009f0eDd6211231) | + +## nUSD + +| Chain | Address | +|-----------|----------------------------------------------| +| Arbitrum | `0x2913e812cf0dcca30fb28e6cac3d2dcff4497688` [↗](https://arbiscan.io/token/0x2913e812cf0dcca30fb28e6cac3d2dcff4497688) | +| Aurora | `0x07379565cD8B0CaE7c60Dc78e7f601b34AF2A21c` [↗](https://explorer.mainnet.aurora.dev/address/0x07379565cD8B0CaE7c60Dc78e7f601b34AF2A21c/transactions) | +| Avalanche | `0xCFc37A6AB183dd4aED08C204D1c2773c0b1BDf46` [↗](https://snowtrace.io/address/0xCFc37A6AB183dd4aED08C204D1c2773c0b1BDf46) | +| Blast | `0x3194B0A295D87fDAA54DF852c248F7a6BAF6c6e0` [↗](https://blastscan.io/address/0x3194B0A295D87fDAA54DF852c248F7a6BAF6c6e0) | +| Boba | `0x6B4712AE9797C199edd44F897cA09BC57628a1CF` [↗](https://blockexplorer.boba.network/tokens/0x6B4712AE9797C199edd44F897cA09BC57628a1CF/token-transfers) | +| BSC | `0x23b891e5c62e0955ae2bd185990103928ab817b3` [↗](https://bscscan.com/token/0x23b891e5c62e0955ae2bd185990103928ab817b3) | +| Cronos | `0x396c9c192dd323995346632581BEF92a31AC623b` [↗](https://cronoscan.com/address/0x396c9c192dd323995346632581BEF92a31AC623b) | +| DFK | `0x52285D426120aB91F378b3dF4A15a036a62200aE` [↗](https://subnets.avax.network/defi-kingdoms/dfk-chain/explorer/address/0x52285D426120aB91F378b3dF4A15a036a62200aE) | +| Ethereum | `0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F` [↗](https://etherscan.io/token/0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F) | +| Fantom | `0xed2a7edd7413021d440b09d654f3b87712abab66` [↗](https://ftmscan.com/token/0xed2a7edd7413021d440b09d654f3b87712abab66) | +| Harmony | `0xed2a7edd7413021d440b09d654f3b87712abab66` [↗](https://explorer.harmony.one/address/0xed2a7edd7413021d440b09d654f3b87712abab66) | +| Optimism | `0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00` [↗](https://optimistic.etherscan.io/address/0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00) | +| Polygon | `0xb6c473756050de474286bed418b77aeac39b02af` [↗](https://polygonscan.com/token/0xb6c473756050de474286bed418b77aeac39b02af) | +| Metis | `0x961318fc85475e125b99cc9215f62679ae5200ab` [↗](https://andromeda-explorer.metis.io/address/0x961318Fc85475E125B99Cc9215f62679aE5200aB) | diff --git a/docs/bridge/docs/05-Contracts/04-Liquidity-Pools.md b/docs/bridge/docs/05-Contracts/04-Liquidity-Pools.md new file mode 100644 index 0000000000..5f3b5a887e --- /dev/null +++ b/docs/bridge/docs/05-Contracts/04-Liquidity-Pools.md @@ -0,0 +1,41 @@ +--- +title: Liquidity Pools +--- + +:::note This list may be incomplete + +The canonical list is hosted within the SynapseCNS on [Github](https://github.com/synapsecns/synapse-contracts). + +::: + +# Liquidity Pools + +Liquidity pools are available at [https://synapseprotocol.com/pools](https://synapseprotocol.com/pools) + +## ETH Pools + +| Chain | Address | +|-----------|----------------------------------------------| +| Arbitrum | `0xa067668661C84476aFcDc6fA5D758C4c01C34352` [↗](https://arbiscan.io/address/0x6f4e8eba4d337f874ab57478acc2cb5bacdc19c9) | +| Avalanche | `0x77a7e60555bC18B4Be44C181b2575eee46212d44` [↗](https://snowtrace.io/address/0x77a7e60555bC18B4Be44C181b2575eee46212d44) | +| Base | `0x6223bD82010E2fB69F329933De20897e7a4C225f` [↗](https://basescan.org/address/0x6223bd82010e2fb69f329933de20897e7a4c225f) | +| Blast | `0x999fcd13C54B26E02a6Ccd185f71550b3a4641c0` [↗](https://blastscan.io/address/0x999fcd13C54B26E02a6Ccd185f71550b3a4641c0) | +| Metis | `0x09fEC30669d63A13c666d2129230dD5588E2e240` [↗](https://andromeda-explorer.metis.io/address/0x09fEC30669d63A13c666d2129230dD5588E2e240) | +| Optimism | `0xE27BFf97CE92C3e1Ff7AA9f86781FDd6D48F5eE9` [↗](https://optimistic.etherscan.io/address/0xE27BFf97CE92C3e1Ff7AA9f86781FDd6D48F5eE9) | + +## Stableswap Pools + +| Chain | Address | +|-----------|----------------------------------------------| +| Arbitrum | `0x9Dd329F5411466d9e0C488fF72519CA9fEf0cb40` [↗](https://arbiscan.io/address/0x9Dd329F5411466d9e0C488fF72519CA9fEf0cb40) | +| Aurora | `0x3CE7AAD78B9eb47Fd2b487c463A17AAeD038B7EC` [↗](https://explorer.aurora.dev/address/0x3CE7AAD78B9eb47Fd2b487c463A17AAeD038B7EC) | +| Avalanche | `0xED2a7edd7413021d440b09D654f3b87712abAB66` [↗](https://snowtrace.io/address/0xED2a7edd7413021d440b09D654f3b87712abAB66) | +| Blast | `0xa4bd1AAD7cF04567c10f38FC4355E91bba32aC9c` [↗](https://blastscan.io/address/0xa4bd1AAD7cF04567c10f38FC4355E91bba32aC9c) | +| BNB Chain | `0x28ec0B36F0819ecB5005cAB836F4ED5a2eCa4D13` [↗](https://bscscan.com/address/0x28ec0B36F0819ecB5005cAB836F4ED5a2eCa4D13) | +| Boba | `0x75FF037256b36F15919369AC58695550bE72fead` [↗](https://bobascan.com/address/0x75FF037256b36F15919369AC58695550bE72fead) | +| Canto | `0x07379565cD8B0CaE7c60Dc78e7f601b34AF2A21c` [↗](https://evm.explorer.canto.io/address/0x07379565cD8B0CaE7c60Dc78e7f601b34AF2A21c) | +| Ethereum | `0x1116898DdA4015eD8dDefb84b6e8Bc24528Af2d8` [↗](https://etherscan.io/address/0x1116898DdA4015eD8dDefb84b6e8Bc24528Af2d8) | +| Fantom | `0x85662fd123280827e11C59973Ac9fcBE838dC3B4` [↗](https://ftmscan.com/address/0x85662fd123280827e11C59973Ac9fcBE838dC3B4) | +| Metis | `0x555982d2E211745b96736665e19D9308B615F78e` [↗](https://andromeda-explorer.metis.io/address/0x555982d2E211745b96736665e19D9308B615F78e) | +| Optimism | `0xF44938b0125A6662f9536281aD2CD6c499F22004` [↗](https://optimistic.etherscan.io/address/0xf44938b0125a6662f9536281ad2cd6c499f22004) | +| Polygon | `0x85fCD7Dd0a1e1A9FCD5FD886ED522dE8221C3EE5` [↗](https://polygonscan.com/address/0x85fCD7Dd0a1e1A9FCD5FD886ED522dE8221C3EE5) | diff --git a/docs/bridge/docs/05-Contracts/05-CCTP.md b/docs/bridge/docs/05-Contracts/05-CCTP.md new file mode 100644 index 0000000000..011bdb90ea --- /dev/null +++ b/docs/bridge/docs/05-Contracts/05-CCTP.md @@ -0,0 +1,21 @@ +:::note This list may be incomplete + +The canonical list is hosted within the SynapseCNS on [Github](https://github.com/synapsecns/synapse-contracts). + +::: + +# CCTP + +Synapse CCTP transactions primarily interact with the Synapse CCTP Router contract, which is responsible for routing transactions with the proper meta data to the Synapse CCTP contract on the relevant chain, which utilizes the base [Circle CCTP contracts](https://developers.circle.com/stablecoins/docs/evm-smart-contracts) to mint and burn USDC across supported chains. + +**Synapse CCTP Router Address**: `0xd5a597d6e7ddf373a92c8f477daaa673b0902f48`\ +**Contract**: [SynapseCCTP.sol](https://github.com/synapsecns/synapse-contracts/blob/master/contracts/cctp/SynapseCCTP.sol) + +| Chain | Address | +| --------- | ---------------------------------------------| +| Arbitrum | `0x12715a66773bd9c54534a01abf01d05f6b4bd35e` [↗](https://arbiscan.io/address/0x12715a66773bd9c54534a01abf01d05f6b4bd35e) | +| Avalanche | `0x12715a66773bd9c54534a01abf01d05f6b4bd35e` [↗](https://snowtrace.io/address/0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E) | +| Base | `0x12715a66773bd9c54534a01abf01d05f6b4bd35e` [↗](https://basescan.org/address/0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E) | +| Ethereum | `0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E` [↗](https://etherscan.io/address/0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E) | +| Optimism | `0x12715a66773bd9c54534a01abf01d05f6b4bd35e` [↗](https://optimistic.etherscan.io/address/0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E) | +| Polygon | `0x12715a66773bd9c54534a01abf01d05f6b4bd35e` [↗](https://polygonscan.com/address/0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E) | diff --git a/docs/bridge/docs/05-Contracts/06-RFQ.md b/docs/bridge/docs/05-Contracts/06-RFQ.md new file mode 100644 index 0000000000..3e9691a1d2 --- /dev/null +++ b/docs/bridge/docs/05-Contracts/06-RFQ.md @@ -0,0 +1,28 @@ +--- +title: RFQ +--- + +:::note This list may be incomplete + +The canonical list is hosted within the SynapseCNS on [Github](https://github.com/synapsecns/synapse-contracts). + +::: + +# RFQ + +RFQ transactions primarily interact with the Router Address, which is responsible for routing transactions with the proper meta data to the RFQ contract on the relevant chain. + +**Source code**: [SynapseCNS (Github)](https://github.com/synapsecns/sanguine/tree/master/packages/contracts-rfq)\ +**Generated docs**: [RFQ docs](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html)\ +**RFQ Router Address**: `0x00cD000000003f7F682BE4813200893d4e690000` + +| Chain | Address | +| -------- | ------- | +| Arbitrum | `0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E` [↗](https://arbiscan.io/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | +| Base | `0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E` [↗](https://basescan.org/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | +| Ethereum | `0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E` [↗](https://etherscan.io/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | +| Optimism | `0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E` [↗](https://optimistic.etherscan.io/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | +| Scroll | `0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E` [↗](https://scrollscan.com/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | +| Linea | `0x34F52752975222d5994C206cE08C1d5B329f24dD` [↗](https://lineascan.build/address/0x34F52752975222d5994C206cE08C1d5B329f24dD) | +| BNB Chain| `0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E` [↗](https://bscscan.com/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | +| Blast | `0x34F52752975222d5994C206cE08C1d5B329f24dD` [↗](https://blastscan.io/address/0x34F52752975222d5994C206cE08C1d5B329f24dD) | diff --git a/docs/bridge/docs/05-Contracts/07-Bridge-Zaps.md b/docs/bridge/docs/05-Contracts/07-Bridge-Zaps.md new file mode 100644 index 0000000000..e5e0bcc6d6 --- /dev/null +++ b/docs/bridge/docs/05-Contracts/07-Bridge-Zaps.md @@ -0,0 +1,30 @@ +--- +title: Bridge Zaps +--- + +:::note This list may be incomplete + +The canonical list is hosted within the SynapseCNS on [Github](https://github.com/synapsecns/synapse-contracts). + +::: + +# Bridge Zaps + +| Chain | Address | +|-----------|----------------------------------------------| +| Arbitrum | `0x37f9aE2e0Ea6742b9CAD5AbCfB6bBC3475b3862B` [↗](https://arbiscan.io/address/0x37f9aE2e0Ea6742b9CAD5AbCfB6bBC3475b3862B) | +| Aurora | `0x2D8Ee8d6951cB4Eecfe4a79eb9C2F973C02596Ed` [↗](https://aurorascan.dev/address/0x2D8Ee8d6951cB4Eecfe4a79eb9C2F973C02596Ed) | +| Avalanche | `0x0EF812f4c68DC84c22A4821EF30ba2ffAB9C2f3A` [↗](https://snowtrace.io/address/0x0EF812f4c68DC84c22A4821EF30ba2ffAB9C2f3A) | +| Boba | `0x64B4097bCCD27D49BC2A081984C39C3EeC427a2d` [↗](https://blockexplorer.boba.network/address/0x64B4097bCCD27D49BC2A081984C39C3EeC427a2d/transactions) | +| BSC | `0x749F37Df06A99D6A8E065dd065f8cF947ca23697` [↗](https://bscscan.com/address/0x749F37Df06A99D6A8E065dd065f8cF947ca23697) | +| Canto | `0x8671A0465844a15eb7230C5dd8d6032c26c655B7` [↗](https://evm.explorer.canto.io/address/0x8671A0465844a15eb7230C5dd8d6032c26c655B7) | +| Cronos | `0x991adb00eF4c4a6D1eA6036811138Db4379377C2` [↗](https://cronoscan.com/address/0x991adb00eF4c4a6D1eA6036811138Db4379377C2) | +| DFK | `0x75224b0f245Fe51d5bf47A898DbB6720D4150BA7` [↗](https://subnets.avax.network/defi-kingdoms/dfk-chain/explorer/address/0x75224b0f245Fe51d5bf47A898DbB6720D4150BA7) | +| Dogechain | `0x544450Ffdfa5EA20528F21918E8aAC7B2C733381` [↗](https://explorer.dogechain.dog/address/0x544450Ffdfa5EA20528F21918E8aAC7B2C733381) | +| Ethereum | `0x6571d6be3d8460CF5F7d6711Cd9961860029D85F` [↗](https://etherscan.io/address/0x6571d6be3d8460CF5F7d6711Cd9961860029D85F) | +| Fantom | `0xB003e75f7E0B5365e814302192E99b4EE08c0DEd` [↗](https://ftmscan.com/address/0xB003e75f7E0B5365e814302192E99b4EE08c0DEd) | +| Harmony | `0xB003e75f7E0B5365e814302192E99b4EE08c0DEd` [↗](https://explorer.harmony.one/address/0xb003e75f7e0b5365e814302192e99b4ee08c0ded) | +| Optimism | `0x470f9522ff620eE45DF86C58E54E6A645fE3b4A7` [↗](https://optimistic.etherscan.io/address/0x470f9522ff620eE45DF86C58E54E6A645fE3b4A7) | +| Moonbeam | `0x73783F028c60D463bc604cc53852C37C31dEC5e9` [↗](https://moonscan.io/address/0x73783F028c60D463bc604cc53852C37C31dEC5e9) | +| Moonriver | `0x06Fea8513FF03a0d3f61324da709D4cf06F42A5c` [↗](https://moonriver.moonscan.io/address/0x06Fea8513FF03a0d3f61324da709D4cf06F42A5c) | +| Polygon | `0x1c6aE197fF4BF7BA96c66C5FD64Cb22450aF9cC8` [↗](https://polygonscan.com/address/0x1c6aE197fF4BF7BA96c66C5FD64Cb22450aF9cC8) | diff --git a/docs/bridge/docs/05-Contracts/08-MiniChef.md b/docs/bridge/docs/05-Contracts/08-MiniChef.md new file mode 100644 index 0000000000..9484a68f79 --- /dev/null +++ b/docs/bridge/docs/05-Contracts/08-MiniChef.md @@ -0,0 +1,29 @@ +--- +title: MiniChef +--- + +:::note This list may be incomplete + +The canonical list is hosted within the SynapseCNS on [Github](https://github.com/synapsecns/synapse-contracts). + +::: + +# MiniChef + +| Chain | Address | +|-----------|----------------------------------------------| +| Arbitrum | `0x73186f2Cf2493f20836b17b21ae79fc12934E207` [↗](https://arbiscan.io/address/0x73186f2Cf2493f20836b17b21ae79fc12934E207) | +| Aurora | `0x809DC529f07651bD43A172e8dB6f4a7a0d771036` [↗](https://explorer.mainnet.aurora.dev/address/0x809DC529f07651bD43A172e8dB6f4a7a0d771036/transactions) | +| Avalanche | `0x3a01521F8E7F012eB37eAAf1cb9490a5d9e18249` [↗](https://snowtrace.io/address/0x3a01521F8E7F012eB37eAAf1cb9490a5d9e18249) | +| Base | `0xfFC2d603fde1F99ad94026c00B6204Bb9b8c36E9` [↗](https://basescan.org/address/0xfFC2d603fde1F99ad94026c00B6204Bb9b8c36E9) | +| Blast | `0x3100dC8464A8523306c3C5034de24a8927d6E590` [↗](https://blastscan.io/address/0x3100dC8464A8523306c3C5034de24a8927d6E590) | +| Boba | `0xd5609cD0e1675331E4Fb1d43207C8d9D83AAb17C` [↗](https://blockexplorer.boba.network/address/0xd5609cD0e1675331E4Fb1d43207C8d9D83AAb17C/transactions) | +| BSC | `0x8F5BBB2BB8c2Ee94639E55d5F41de9b4839C1280` [↗](https://bscscan.com/address/0x8F5BBB2BB8c2Ee94639E55d5F41de9b4839C1280) | +| Canto | `0x93124c923dA389Bc0f13840fB822Ce715ca67ED6` [↗](https://canto.dex.guru/address/0x93124c923dA389Bc0f13840fB822Ce715ca67ED6) | +| Ethereum | `0xd10eF2A513cEE0Db54E959eF16cAc711470B62cF` [↗](https://etherscan.io/address/0xd10eF2A513cEE0Db54E959eF16cAc711470B62cF) | +| Fantom | `0xaeD5b25BE1c3163c907a471082640450F928DDFE` [↗](https://ftmscan.com/address/0xaed5b25be1c3163c907a471082640450f928ddfe) | +| Harmony | `0xaeD5b25BE1c3163c907a471082640450F928DDFE` [↗](https://explorer.harmony.one/address/0xaed5b25be1c3163c907a471082640450f928ddfe) | +| Metis | `0xaB0D8Fc46249DaAcd5cB36c5F0bC4f0DAF34EBf5` [↗](https://andromeda-explorer.metis.io/address/0xaB0D8Fc46249DaAcd5cB36c5F0bC4f0DAF34EBf5) | +| Moonriver | `0x432036208d2717394d2614d6697c46DF3Ed69540` [↗](https://moonriver.moonscan.io/address/0x432036208d2717394d2614d6697c46DF3Ed69540) | +| Optimism | `0xe8c610fcb63A4974F02Da52f0B4523937012Aaa0` [↗](https://optimistic.etherscan.io/address/0xe8c610fcb63A4974F02Da52f0B4523937012Aaa0) | +| Polygon | `0x7875Af1a6878bdA1C129a4e2356A3fD040418Be5` [↗](https://polygonscan.com/address/0x7875Af1a6878bdA1C129a4e2356A3fD040418Be5) | diff --git a/docs/bridge/docs/Services/Scribe.md b/docs/bridge/docs/06-Services/01-Scribe.md similarity index 95% rename from docs/bridge/docs/Services/Scribe.md rename to docs/bridge/docs/06-Services/01-Scribe.md index 0b58a1edab..6af5fd2de8 100644 --- a/docs/bridge/docs/Services/Scribe.md +++ b/docs/bridge/docs/06-Services/01-Scribe.md @@ -1,8 +1,3 @@ ---- -sidebar_position: 0 -sidebar_label: Scribe ---- - # Scribe Scribe is a multi-chain indexing service designed to store logs, receipts, and transactions for every event from specified contracts across multiple blockchains. It provides a powerful tool for analytics, event streaming, and monitoring on-chain activities. @@ -89,7 +84,7 @@ chains: Key configuration parameters include: -- `rpc_url`: The omnirpc url to use for querying chain data (no trailing slash). For more information on omnirpc, see [here](Services//Omnirpc.md). +- `rpc_url`: The omnirpc url to use for querying chain data (no trailing slash). For more information on omnirpc, see [here](Omnirpc). - `chains`: List of chains to index, including chain-specific parameters: - `chain_id`: The ID of the chain. - `get_logs_range`: The number of blocks to request in a single `getLogs` request. @@ -117,4 +112,4 @@ For a full list of available queries, refer to the GraphQL schema. ## Observability -Scribe implements open telemetry for both tracing and metrics. Please see the [Observability](../Observability) page for more info. +Scribe implements open telemetry for both tracing and metrics. Please see the [Observability](Observability) page for more info. diff --git a/docs/bridge/docs/Services/Omnirpc.md b/docs/bridge/docs/06-Services/02-Omnirpc.md similarity index 89% rename from docs/bridge/docs/Services/Omnirpc.md rename to docs/bridge/docs/06-Services/02-Omnirpc.md index e719fc0278..f0b804f104 100644 --- a/docs/bridge/docs/Services/Omnirpc.md +++ b/docs/bridge/docs/06-Services/02-Omnirpc.md @@ -1,10 +1,12 @@ -Omnirpc is an rpc load balancer and verifier that allows users to query chain data from multiple chains. It is a service that should be run by Quoters and interfaces that allow Solvers to post quotes. Omnirpc takes in a yaml config that allows the user to specify which chains it should run on. +# OmniRPC -### Running OmniRPC +OmniRPC is an RPC load balancer and verifier that allows users to query chain data from multiple chains. It is a service that should be run by Quoters and interfaces that allow Solvers to post quotes. OmniRPC takes in a yaml config that allows the user to specify which chains it should run on. + +## Running OmniRPC ### Building From Source -To build omnirpc from source, you will need to have Go installed. You can install Go by following the instructions [here](https://golang.org/doc/install). Once you have Go installed, you can build the relayer by running the following commands: +To build OmniRPC from source, you will need to have Go installed. You can install Go by following the instructions [here](https://golang.org/doc/install). Once you have Go installed, you can build the relayer by running the following commands: 1. `git clone https://github.com/synapsecns/sanguine --recursive` 2. `cd sanguine/services/omnirpc` @@ -18,11 +20,11 @@ The relayer can also be run with docker. To do this, you will need to pull the [ docker run ghcr.io/synapsecns/sanguine/omnirpc:latest --config /path/to/config ``` -There is also a helm chart available for omnirpc [here](https://artifacthub.io/packages/helm/synapse/omnirpc). +There is also a helm chart available for OmniRPC [here](https://artifacthub.io/packages/helm/synapse/omnirpc). ### Configuration -Omnirpc is configured with a yaml file. The following is an example configuration: +OmniRPC is configured with a yaml file. The following is an example configuration: ```yaml chains: diff --git a/docs/bridge/docs/Services/Signer.md b/docs/bridge/docs/06-Services/03-Signer.md similarity index 100% rename from docs/bridge/docs/Services/Signer.md rename to docs/bridge/docs/06-Services/03-Signer.md diff --git a/docs/bridge/docs/Services/Submitter.md b/docs/bridge/docs/06-Services/04-Submitter.md similarity index 98% rename from docs/bridge/docs/Services/Submitter.md rename to docs/bridge/docs/06-Services/04-Submitter.md index 379145a999..afa6435d42 100644 --- a/docs/bridge/docs/Services/Submitter.md +++ b/docs/bridge/docs/06-Services/04-Submitter.md @@ -1,6 +1,8 @@ -# Submitter +:::note In-progress -This section is still in progress, please see [here](https://pkg.go.dev/github.com/synapsecns/sanguine/ethergo/submitter#section-readme) for details. +Please see the [Go Submitter documentation page](https://pkg.go.dev/github.com/synapsecns/sanguine/ethergo/submitter#section-readme) for more details. + +::: # Ethergo Submitter diff --git a/docs/bridge/docs/Observability.md b/docs/bridge/docs/06-Services/05-Observability.md similarity index 99% rename from docs/bridge/docs/Observability.md rename to docs/bridge/docs/06-Services/05-Observability.md index 3a9fb2fadd..69466e087f 100644 --- a/docs/bridge/docs/Observability.md +++ b/docs/bridge/docs/06-Services/05-Observability.md @@ -1,3 +1,5 @@ +# Observability + All off-chain systems are by default observable and configured through the [metrics](https://pkg.go.dev/github.com/synapsecns/sanguine/core/metrics#section-readme) package. The observability stack is built around [open telemetry](https://opentelemetry.io/) with metrics also being exported using this standard. "Metrics" themselves are divided into 3 different types of metrics: diff --git a/docs/bridge/docs/06-Services/06-RFQ-Indexer-API.md b/docs/bridge/docs/06-Services/06-RFQ-Indexer-API.md new file mode 100644 index 0000000000..1cbb3ddca3 --- /dev/null +++ b/docs/bridge/docs/06-Services/06-RFQ-Indexer-API.md @@ -0,0 +1,14 @@ +# RFQ Indexer API + +The RFQ Indexer API is a service designed to provide access to indexed RFQ bridge event data. It offers crucial monitoring and operational capabilities, serving as a supplemental tool to existing relayer infrastructure. This API is particularly useful for tracking pending bridge transactions and identifying missing relays, proofs, or claims. + +## API-docs + +[`https://triumphant-magic-production.up.railway.app/api-docs/`](https://triumphant-magic-production.up.railway.app/api-docs/) + +## Key Features +1. **Real-Time and Historical Data** Indexes from a specified start block up to real-time events. +2. **On-chain Tracing** Tracks all on-chain transactions and events, helping to debug. +4. **GraphQL API**: Provides a GraphQL/IQL endpoint for easy data querying. + + diff --git a/docs/bridge/docs/Services/img/signer/aws/create-acces-key.png b/docs/bridge/docs/06-Services/img/signer/aws/create-acces-key.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/create-acces-key.png rename to docs/bridge/docs/06-Services/img/signer/aws/create-acces-key.png diff --git a/docs/bridge/docs/Services/img/signer/aws/create-access-key.png b/docs/bridge/docs/06-Services/img/signer/aws/create-access-key.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/create-access-key.png rename to docs/bridge/docs/06-Services/img/signer/aws/create-access-key.png diff --git a/docs/bridge/docs/Services/img/signer/aws/iam-dash.png b/docs/bridge/docs/06-Services/img/signer/aws/iam-dash.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/iam-dash.png rename to docs/bridge/docs/06-Services/img/signer/aws/iam-dash.png diff --git a/docs/bridge/docs/Services/img/signer/aws/iam-preview-user.png b/docs/bridge/docs/06-Services/img/signer/aws/iam-preview-user.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/iam-preview-user.png rename to docs/bridge/docs/06-Services/img/signer/aws/iam-preview-user.png diff --git a/docs/bridge/docs/Services/img/signer/aws/key-details.png b/docs/bridge/docs/06-Services/img/signer/aws/key-details.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/key-details.png rename to docs/bridge/docs/06-Services/img/signer/aws/key-details.png diff --git a/docs/bridge/docs/Services/img/signer/aws/kms-1.png b/docs/bridge/docs/06-Services/img/signer/aws/kms-1.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/kms-1.png rename to docs/bridge/docs/06-Services/img/signer/aws/kms-1.png diff --git a/docs/bridge/docs/Services/img/signer/aws/kms-2.png b/docs/bridge/docs/06-Services/img/signer/aws/kms-2.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/kms-2.png rename to docs/bridge/docs/06-Services/img/signer/aws/kms-2.png diff --git a/docs/bridge/docs/Services/img/signer/aws/kms-advanced.png b/docs/bridge/docs/06-Services/img/signer/aws/kms-advanced.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/kms-advanced.png rename to docs/bridge/docs/06-Services/img/signer/aws/kms-advanced.png diff --git a/docs/bridge/docs/Services/img/signer/aws/kms-labels.png b/docs/bridge/docs/06-Services/img/signer/aws/kms-labels.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/kms-labels.png rename to docs/bridge/docs/06-Services/img/signer/aws/kms-labels.png diff --git a/docs/bridge/docs/Services/img/signer/aws/kms-permissons.png b/docs/bridge/docs/06-Services/img/signer/aws/kms-permissons.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/kms-permissons.png rename to docs/bridge/docs/06-Services/img/signer/aws/kms-permissons.png diff --git a/docs/bridge/docs/Services/img/signer/aws/kms-user-permissions.png b/docs/bridge/docs/06-Services/img/signer/aws/kms-user-permissions.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/kms-user-permissions.png rename to docs/bridge/docs/06-Services/img/signer/aws/kms-user-permissions.png diff --git a/docs/bridge/docs/Services/img/signer/aws/kms-user.png b/docs/bridge/docs/06-Services/img/signer/aws/kms-user.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/kms-user.png rename to docs/bridge/docs/06-Services/img/signer/aws/kms-user.png diff --git a/docs/bridge/docs/Services/img/signer/aws/perms.png b/docs/bridge/docs/06-Services/img/signer/aws/perms.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/perms.png rename to docs/bridge/docs/06-Services/img/signer/aws/perms.png diff --git a/docs/bridge/docs/Services/img/signer/aws/review.png b/docs/bridge/docs/06-Services/img/signer/aws/review.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/review.png rename to docs/bridge/docs/06-Services/img/signer/aws/review.png diff --git a/docs/bridge/docs/Services/img/signer/aws/user-list.png b/docs/bridge/docs/06-Services/img/signer/aws/user-list.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/user-list.png rename to docs/bridge/docs/06-Services/img/signer/aws/user-list.png diff --git a/docs/bridge/docs/Services/img/signer/aws/user-perms.png b/docs/bridge/docs/06-Services/img/signer/aws/user-perms.png similarity index 100% rename from docs/bridge/docs/Services/img/signer/aws/user-perms.png rename to docs/bridge/docs/06-Services/img/signer/aws/user-perms.png diff --git a/docs/bridge/docs/Services/img/submitter/metrics.png b/docs/bridge/docs/06-Services/img/submitter/metrics.png similarity index 100% rename from docs/bridge/docs/Services/img/submitter/metrics.png rename to docs/bridge/docs/06-Services/img/submitter/metrics.png diff --git a/docs/bridge/docs/07-Support/Transaction-Support.md b/docs/bridge/docs/07-Support/Transaction-Support.md new file mode 100644 index 0000000000..5ebf4ccbb9 --- /dev/null +++ b/docs/bridge/docs/07-Support/Transaction-Support.md @@ -0,0 +1,51 @@ +# Transaction Support FAQ + +## What does a Bridge transaction look like? + +After submitting and signing a transaction from your wallet, gas fees are collected, and the transaction is sent to the origin chain router. Once accepted, the bridged asset is removed from your portfolio, and a progress bar shows the estimated confirmation time. + +Once confirmed on the destination chain, the asset is added to your portfolio, and destination hash is available from the progress menu dropdown. The transaction appears as part of your history in the Activity tab once it is index by the Synapse Explorer. + +Gas token airdrops and rebates are delivered to your wallet automatically. However, only bridgeable assets are shown in your Synapse portfolio. + +## Did my transaction initiate? + +Transactions that do not initiate on the origin chain return an error message. Your assets will remain in your portfolio, under your control. + +In the event that your transaction does not initiate, double check that you have sufficient funds to send, and to cover gas fees, and you can safely try again. + +## My transaction failed to initiate after several tries + +Occasionally, technical issues or a high volume of activity on the origin chain may prevent new transactions from being accepted. + +In most cases, these issues safely resolve within 30-60 minutes. Activity levels can be found on native block explorers (e.g the [Etherscan gas tracker](https://etherscan.io/gastracker)). + +You can also adjust your wallet’s gas settings to make transactions more likely to be accepted during times of peak activity. + +## Why is my transaction taking so long? +Synapse time estimates are based on destination block times. Occasionally, a transaction may post to a later block than expected. + +Block explorer links in the progress dropdown menu can confirm whether a confirmation on-chain but not yet received by Synapse. + +## My transaction failed to complete + +Transactions that fail to complete are not lost, and are manually addressed by the Synapse support team. You can reach Support via the [Synapse Discord channel](https://discord.com/invite/synapseprotocol) at any time. + +:::note For DeFi Kingdoms + +NFT transactions can take twice as long as tokens. Contact Support if your transaction has been pending for two hours or more. + +::: + +## I received a different asset than expected +In the event of an sudden increase in slippage, Synapse will deliver the intermediate asset sent to the destination chain instead of swapping it for an unexpected loss. + +This asset appears in your portfolio and can be safely [swapped](https://synapseprotocol.com/swap) for the asset of your choice on the destination chain. + +## Did I receive my rebate or gas airdrop? +While rebates and airdrops appear in your wallet automatically, only bridgeable assets are shown in your Synapse portfolio. + +If you don’t see an asset you should have received, first check your wallet while connected to the destination chain for your bridge transaction. + +## Help! +Don’t panic! Contact Synapse Support on Discord to answer any other questions you might have. diff --git a/docs/bridge/docs/07-Support/index.md b/docs/bridge/docs/07-Support/index.md new file mode 100644 index 0000000000..12ad2e4940 --- /dev/null +++ b/docs/bridge/docs/07-Support/index.md @@ -0,0 +1,12 @@ +# Support + +Connect with other developers and the Synapse team + +* **[Discord](https://discord.gg/synapseprotocol)** +* **[Twitter](https://twitter.com/SynapseProtocol)** +* **[Telegram](https://t.me/synapseprotocol)** +* **[Forum](https://forum.synapseprotocol.com/)** + +## Frequently Asked Questions + +* [Transaction Support FAQ](Transaction-Support) diff --git a/docs/bridge/docs/CCTP/Contracts.md b/docs/bridge/docs/CCTP/Contracts.md deleted file mode 100644 index 5707af69a6..0000000000 --- a/docs/bridge/docs/CCTP/Contracts.md +++ /dev/null @@ -1,18 +0,0 @@ -# Contracts - -Synapse CCTP contracts deployed on several chains and are documented inline. Synapse CCTP routes tokens through the CCTP module and [SynapseCCTP](https://github.com/synapsecns/synapse-contracts/blob/master/contracts/cctp/SynapseCCTP.sol) interacts with the Circle contracts to mint/burn USDC. These contracts sit on top of the Circle CCTP contracts and are responsible for minting and burning USDC on supported chains. - -### Synapse CCTP - -| Chain | Address | -| --------- | -------------------------------------------------------------------------------------------------------------------------------- | -| Arbitrum | [0x12715a66773bd9c54534a01abf01d05f6b4bd35e](https://arbiscan.io/address/0x12715a66773bd9c54534a01abf01d05f6b4bd35e) | -| Avalanche | [0x12715a66773bd9c54534a01abf01d05f6b4bd35e](https://snowtrace.io/address/0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E) | -| Base | [0x12715a66773bd9c54534a01abf01d05f6b4bd35e](https://basescan.org/address/0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E) | -| Ethereum | [0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E](https://etherscan.io/address/0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E) | -| Optimism | [0x12715a66773bd9c54534a01abf01d05f6b4bd35e](https://optimistic.etherscan.io/address/0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E) | -| Polygon | [0x12715a66773bd9c54534a01abf01d05f6b4bd35e](https://polygonscan.com/address/0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E) | - -## Circle Contracts - -Please refer to [this page](https://developers.circle.com/stablecoins/docs/evm-smart-contracts) for Circle contract addresses. diff --git a/docs/bridge/docs/CCTP/Overview.md b/docs/bridge/docs/CCTP/Overview.md deleted file mode 100644 index cc6f4b1fc8..0000000000 --- a/docs/bridge/docs/CCTP/Overview.md +++ /dev/null @@ -1,8 +0,0 @@ -Synapse CCTP is a custom module built on top of Circle's [Cross-Chain Transfer Protocol](https://www.circle.com/en/cross-chain-transfer-protocol) that allows for bridge requests to natively mint & burn USDC on [supported chains](https://developers.circle.com/stablecoins/docs/cctp-getting-started#supported-blockchains). - -Synapse's CCTP implementation consists of two main components: - -- [CCTP Relayer](./Relayer.md): An off-chain service that fulfills transactions requested through the CCTP contracts. The relayer is responsible for fetching attestations from the [Circle API](https://developers.circle.com/stablecoin/reference) and submitting them to the CCTP contracts. Anyone can run a relayer. -- [CCTP Contracts](./Contracts.md): A set of smart contracts that allow for the minting and burning of USDC on supported chains, and instant swaps to/from any supported asset. These contracts are deployed on each supported chain and are responsible for minting and burning USDC. - -As a modular component of Synapse's router system, CCTP can be configured to bridge through any supported liquidity source, such as [Curve](https://github.com/synapsecns/synapse-contracts/blob/885cbe06a960591b1bdef330f3d3d57c49dba8e2/contracts/router/modules/pool/curve/CurveV1Module.sol), [Algebra](https://github.com/synapsecns/synapse-contracts/blob/885cbe06a960591b1bdef330f3d3d57c49dba8e2/contracts/router/modules/pool/algebra/AlgebraModule.sol), [DAI PSM](https://github.com/synapsecns/synapse-contracts/blob/885cbe06a960591b1bdef330f3d3d57c49dba8e2/contracts/router/modules/pool/dss/DssPsmModule.sol), and others. diff --git a/docs/bridge/docs/CCTP/Relayer.md b/docs/bridge/docs/CCTP/Relayer.md deleted file mode 100644 index 35a309cbfc..0000000000 --- a/docs/bridge/docs/CCTP/Relayer.md +++ /dev/null @@ -1,107 +0,0 @@ -# CCTP Relayer - -The CCTP relayer is an off-chain service aimed at fulfilling transactions requested through the [CCTP Contracts](./Contracts.md). The relayer is responsible for fetching attestations from the [Circle API](https://developers.circle.com/stablecoin/reference) and submitting them to the CCTP contracts. Anyone can run a relayer. - -### Architecture - -The relayer is a Golang application that polls for events on chain and uses a combo state (db status) and event (on-chain logs) driven [architecture](https://medium.com/@matt.denobrega/state-vs-event-based-web-architectures-59ab1f47656b) to process transactions. The relayer is designed to be run by anyone and be easily observable. - -At a high level, the relayer works like this: - -1. Poll for new transactions from the CCTP contracts and add them to the database as [Pending](https://pkg.go.dev/github.com/synapsecns/sanguine/services/cctp-relayer@v0.10.0/types#MessageState) -2. Fetch the attestation from the Circle API. Once successful add attestation to the database and update status to be [Attested](https://pkg.go.dev/github.com/synapsecns/sanguine/services/cctp-relayer@v0.10.0/types#MessageState) -3. Submit the attestation to the CCTP contracts. Once the transaction has been added to [Submitter](../Services/Submitter#Observability), mark as [Submitted](https://pkg.go.dev/github.com/synapsecns/sanguine/services/cctp-relayer@v0.10.0/types#MessageState) -4. Poll for the transaction receipt and mark as [Confirmed](https://pkg.go.dev/github.com/synapsecns/sanguine/services/cctp-relayer@v0.10.0/types#MessageState) - -### Modes - -As specified by the [cctp_type](#Configuration), the CCTP relayer can be run in one of two modes. In [Synapse mode](https://pkg.go.dev/github.com/synapsecns/sanguine/services/cctp-relayer@v0.10.0/types#MessageType), the [Synapse CCTP](./Contracts.md)contracts are listened to and events relayed through there (including metadata). In [Circle Mode](https://pkg.go.dev/github.com/synapsecns/sanguine/services/cctp-relayer@v0.10.0/types#MessageType), raw [TokenMessenger](https://github.com/circlefin/evm-cctp-contracts/blob/817397db0a12963accc08ff86065491577bbc0e5/src/TokenMessenger.sol) events are relayed. This mode can only be used for USDC to USDC bridges and is not commonly used. - -## Running the Relayer - -### Building From Source - -To build the CCTP Relayer from source, you will need to clone the repository and run the main.go file with the config file. Building from source requires go 1.21 or higher and is generally not recommended for end-users. - -1. `git clone https://github.com/synapsecns/sanguine --recursive` -2. `cd sanguine/services/cctp-relayer` -3. `go run main.go --config /path/to/config.yaml` - -### Running the Docker Image - -The CCTP Relayer can also be run with docker. To do this, you will need to pull the [docker image](https://github.com/synapsecns/sanguine/pkgs/container/sanguine%2Fcctp-relayer) and run it with the config file: - -```bash -docker run ghcr.io/synapsecns/sanguine/cctp-relayer:latest --config /path/to/config -``` - -There is also a helm chart available for the CCTP Relayer [here](https://artifacthub.io/packages/helm/synapse/cctp/0.2.0), but it is recommended you create your own. - -### Configuration - -The CCTP Relayer is configured with a yaml file. The following is an example configuration: - -
- example config -```yaml - cctp_type: "synapse" - # prod contract addresses - chains: - - chain_id: 1 - synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" - - chain_id: 43114 - synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" - - chain_id: 42161 - synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" - - chain_id: 10 - synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" - - chain_id: 8453 - synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" - - chain_id: 137 - synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" - base_omnirpc_url: "http://omnrpc-url/" - unbonded_signer: - type: "AWS" - # should be a mounted secret - file: "/config/aws.txt" - http_backoff_initial_interval_ms: 1000 - http_backoff_max_elapsed_time_ms: 300000 - # submitter config for cctp - submitter_config: - chains: - 1: - supports_eip_1559: true - gas_estimate: 1000000 - 42161: - gas_estimate: 30000000 - max_gas_price: 10000000000 - supports_eip_1559: true - 43114: - gas_estimate: 5000000 - max_gas_price: 1000000000000 - supports_eip_1559: true - 10: - gas_estimate: 5000000 - max_gas_price: 2000000000 - supports_eip_1559: true - 8453: - gas_estimate: 5000000 - 137: - gas_estimate: 5000000 - max_gas_price: 10000000000000 - supports_eip_1559: true -``` -
- - - `cctp_type`: The type of CCTP to listen to. Can be either `synapse` or `circle`. - - `chains`: A list of chain ids and their corresponding CCTP contract addresses. If synapse mode, this should be `synapse_cctp_address` and if circle mode, this should be `token_messenger_address`. Both modes cannot be used at once and the other will be ignored if both are set. - - `base_omnirpc_url`: The base URL for the OmniRPC service. - - `unbonded_signer`: The signer to use for transactions. Can be either `AWS`, `File` or `GCP`. The file should be a mounted secret. More details can be found [here](../Services/Signer). - - `port`: The port to run the relayer on (e.g. 8080) - - `host`: The host to run the relayer on (e.g. localhost). Note: this should not be publicly exposed - - `http_backoff_initial_interval_ms`: The initial interval for the backoff in milliseconds. - - `retry_interval_ms`: The interval to wait between attestation request retries in milliseconds. The [CCTP API Rate Limit](https://developers.circle.com/stablecoins/docs/limits) should be kept in mind. - -### Observability - -The CCTP relayer implements open telemetry for both tracing and metrics. Please see the [Observability](../Observability) page for more info. We'd also highly recommend setting up the [submitter dashboard](../Services/Submitter) as well. diff --git a/docs/bridge/docs/CCTP/_category_.json b/docs/bridge/docs/CCTP/_category_.json deleted file mode 100644 index eda3422018..0000000000 --- a/docs/bridge/docs/CCTP/_category_.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "label": "CCTP", - "position": 3, - "link": { - "type": "doc", - "id": "CCTP/Overview" - } -} diff --git a/docs/bridge/docs/rfq/API/sidebar.ts b/docs/bridge/docs/rfq/API/sidebar.ts deleted file mode 100644 index 3c0e12fc63..0000000000 --- a/docs/bridge/docs/rfq/API/sidebar.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { SidebarsConfig } from "@docusaurus/plugin-content-docs"; - -const sidebar: SidebarsConfig = { - apisidebar: [ - { - type: "category", - label: "quotes", - items: [ - { - type: "doc", - id: "rfq/API/get-quotes", - label: "Get quotes", - className: "api-method get", - }, - { - type: "doc", - id: "rfq/API/upsert-quote", - label: "Upsert quote", - className: "api-method put", - }, - ], - }, - ], -}; - -export default sidebar.apisidebar; diff --git a/docs/bridge/docs/rfq/Contracts.md b/docs/bridge/docs/rfq/Contracts.md deleted file mode 100644 index a5b0947942..0000000000 --- a/docs/bridge/docs/rfq/Contracts.md +++ /dev/null @@ -1,51 +0,0 @@ -### Synapse RFQ - -The Synapse RFQ contract source code can be found [here](https://github.com/synapsecns/sanguine/tree/master/packages/contracts-rfq) along with generated documentation [here](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html) - -| Chain | Address | -| -------- | -------------------------------------------------------------------------------------------------------------------------------- | -| Arbitrum | [0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E](https://arbiscan.io/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | -| Base | [0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E](https://basescan.org/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | -| Ethereum | [0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E](https://etherscan.io/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | -| Optimism | [0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E](https://optimistic.etherscan.io/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | -| Scroll | [0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E](https://scrollscan.com/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | -| Linea | [0x34F52752975222d5994C206cE08C1d5B329f24dD](https://lineascan.build/address/0x34F52752975222d5994C206cE08C1d5B329f24dD) | -| BNB Chain| [0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E](https://bscscan.com/address/0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E) | -| Blast | [0x34F52752975222d5994C206cE08C1d5B329f24dD](https://blastscan.io/address/0x34F52752975222d5994C206cE08C1d5B329f24dD) | - -### On-Chain Architecture & Transaction Flow - -The RFQ contract allows users to post bridge requests based on quotes they have received from the solvers. At a high level, the contract works as follows: - -1. **User calls bridge**: The user calls the bridge contract with the quote they have received from the RFQ API and passing in origin, destination and other paramaters as a [BridgeParam](https://vercel-rfq-docs.vercel.app/contracts/interfaces/IFastBridge.sol/interface.IFastBridge.html#bridgeparams). -2. **Bridge emits event**: The bridge contract emits a [`BridgeRequested`](https://vercel-rfq-docs.vercel.app/contracts/interfaces/IFastBridge.sol/interface.IFastBridge.html#bridgerequested) event. -3. **Solver relays request**: The solver relays the request by calling the [`relay`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#relay) function on the RFQ contract. The contract then pulls the tokens from the solvers wallet (or [msg.value](https://ethereum.stackexchange.com/questions/43362/what-is-msg-value) in the case of eth) and sends them to the user filling their order. -4. **Solver Calls Prove**: The solver then calls the [`prove`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#prove) function on the RFQ contract to prove they have filled the order. In the current implementation, the function must be called from the solver address. -5. **User Claims**: If the solver does not call prove within the optimistic window, the user can call the [`claim`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#claim) function to claim their funds back. - -### On-Chain Statuses - -Like the relayer, each transaction in the RFQ contract has a status. The statuses are as follows: - -| Status | Int | Meaning | -|-----------------|-----|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Null | 0 | Bridge transaction doesn't exist yet on the origin chain. | -| Requested | 1 | A bridge has been requested, but the [`prove`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#prove) has not yet been called | -| Relayer Proved | 2 | The relayer has tried to [`prove`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#prove) the transaction, but cannot claim yet. | -| Relayer Claimed | 3 | The relayer has called [`claim`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#claim) and gotten the funds. | -| Refunded | 4 | The relayer has not called `claim` within the optimistic period or a dispute has been decided in favor of the user and the users been refunded. | - - -### Dispute Period and Guards - -The RFQ system includes a dispute period and guards to ensure the integrity of bridge transactions. Here's how it works: - -After a relayer submits a proof for a bridge transaction, there's a set period during which the transaction can be disputed. This period allows for detection and correction of any errors or fraudulent activities. Guards are responsible for monitoring bridge transactions and initiating disputes if they detect issues such as incorrect fill amounts and proofs submitted by the wrong relayer. A successful dispute would end up with the relayer losing their claimable funds. - -The current implementation is architectured to enforce honest behavior and also protect honest relayers in cases of blockchain reorgs. - -### Other Functionalities - -**ChainGas** - -`sendChainGas` is a field that is populated by the bridge user, and it's a simple bool flag. If `sendChainGas=true` the amount is specified in the FastBridge contract on the destination chain as `chainGasAmount`. This is currently set to zero in all the contracts, and can thus be ignored by filling orders with either no `sendChainGas` option (or to chains with `chainGasAmount==0`) diff --git a/docs/bridge/docs/rfq/RFQ.md b/docs/bridge/docs/rfq/RFQ.md deleted file mode 100644 index d2d9aaf7bd..0000000000 --- a/docs/bridge/docs/rfq/RFQ.md +++ /dev/null @@ -1,24 +0,0 @@ -# RFQ - -RFQ is a bridge module supported by the Synapse Router that allows different market makers to post quotes on different bridge routes. Users can take these quotes by submitting an on-chain bridge request. In the event these requests are not fulfilled, users can request a refund after a set period of time. - -### Actors - -With the exception of the smart contract itself, RFQ is agnostic to how users receive quotes and where Solvers choose to post quotes. Below, we explain who the general actors interacting with the contract are and then explain the canonical RFQ implementation. - -- **Solvers -** Solvers (also known as fillers) are market makers that provide liquidity & post quotes to the API. They are then in charge of fulfilling requests on-chain. -- **Users** - End users observe quotes from solvers and post requests on chain. In the event these requests cannot be fulfilled, the user can reclaim their funds after the optimistic window has passed. -- **Quoter -** The quoter runs a service ran by different interfaces to the Synapse Bridge that allows market makers to post quotes and users to read them. The spec of RFQ does not require this to be an “API” in the traditional sense. Interfaces can use protocols like libp2p, irc and dht’s to communicate quotes. - -Right now, RFQ consists of three-different components, with each of the two off-chain components being ran by different actors: - -- **[API](./API) -** The RFQ api is an off-chain service ran by Quoters. user-interfaces that allows market makers/solvers to post quotes on different bridge routes. Solvers that have registered with the FastBridge contract can sign messages that post quotes signifying at what price they are willing to bridge tokens on a certain route. - - In the canonical implementation, users Solvers authenticated by signing requests with their private key in accordance with [EIP-191](https://eips.ethereum.org/EIPS/eip-191). The canonical implementation can be found [here](https://github.com/synapsecns/sanguine/tree/master/services/rfq). -- **Fast Bridge Contract -** The fast bridge contract is the core of the RFQ protocol and what allows solvers to fulfill requests from users. A user deposits their funds into the FastBridge contract along with the lowest price they are willing to accept for a given route (a price they get by reading quotes from the Quoter). - - In the unlikely event no Solver is available to fulfill a users request, a user can permissionlessly claim their funds back after waiting an optimistic period. - - Contract code level documentation can be found [here](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html). -- **Relayer** - The relayer is a service ran by the solvers. The relayer is responsible for posting quotes & fulfilling requests. While the relayer can be implemented in any way, the canonical implementation is a golang based relayer that provides a way to decide what chains/routes to quote on, how much to quote and which addresses not to relay for. - diff --git a/docs/bridge/docs/rfq/Relayer/Relayer.md b/docs/bridge/docs/rfq/Relayer/Relayer.md deleted file mode 100644 index b47e066ea6..0000000000 --- a/docs/bridge/docs/rfq/Relayer/Relayer.md +++ /dev/null @@ -1,282 +0,0 @@ ---- -sidebar_position: 0 -sidebar_label: Relayer ---- -:::note - -Relayers must be whitelisted in order to fill bridgeRequests. - -::: - -At a high level, the canonical implementation of the relayer has 3 different responsibilities. - -- **Quoting** - Keep track of balances on each chain as well as in-flight funds and continuously post-quotes with these balances using the config to adjust quotes to the solvers specifications and posting to the API. -- **Relaying -** Fulfill users [BridgeRequests](https://vercel-rfq-docs.vercel.app/contracts/interfaces/IFastBridge.sol/interface.IFastBridge.html#bridgerequested) by relaying their funds on-chain. Once eligible, claim the users funds on the origin chain. -- **Rebalancing -** In order to handle the complexity of user flows, the Relayer provides an interface that allows funds to be rebalanced. This allows RFQ to be reflexive to cases where flows are mono-directional. - -## Architecture - -The relayer is a Golang application that polls for events on chain and uses a combo state (db status) and event (on-chain logs) driven architecture to process transactions. The relayer has 3 different event loops going at any given time, specified above and elaborated on below: - -### Quoting -The quoting loop is comparitively simple and updates the api on each route it supports. Quotes are posted using the following formula: - - - **Do not quote above available balance**: Available balance is determined by `balance on chain - in-flight funds`. If the token is the gas token, then the minimum gas token amount is subtracted. The relayer will also not post quotes below the `min_quote_amount` specified in the config. - - **Quote offset**: The quote offset is a percentage of the price of the token. This is used to ensure that the relayer is profitable. The quote offset is added to the price of the token to determine the quote price. - - **Fee**: The fee is determined by the `fixed_fee_multiplier` in the config. This is multiplied by the `origin_gas_estimate` and `destination_gas_estimate` to determine the fee. This fee is added to the quote price. - -### Rebalancing - -Automated rebalancing is configurable by token and currently supports CCTP routes as well as native bridging in the case of Scroll. Rebalancing evaluation follows this logic on every `rebalance_interval`: - -- For every supported rebalance method, compute the 'total balance' of each token as the sum of all balances across chains that support the given rebalance method. -- For every supported token, compute the 'maintenance threshold' as the product of the `maintenance_balance_pct` and the 'total balance'. A token is eligible for rebalancing if the current balance is below the 'maintenance threshold'. - -To limit the amount of inventory that is inflight, only one rebalance can be pending at a time for each token. As such, we select the 'best' rebalance candidate as the rebalance with the largest delta between origin balance and destination balance. - -The final step in evaluating a rebalance is determining the rebalance amount: - -- Arrive at an initial rebalance amount by computing the delta between current balance and the initial threshold on origin. -- Check if this rebalance amount is too much, i.e. it would take the destination balance above its initial threshold. If so, clip the rebalance amount to target the destination initial threshold. -- Filter the rebalance amount by the configured min and max. - -An example of this process is given below: - -We are dealing with chains 1, 2, and 3. Chains 1 and 2 support USDC with CCTP, and chain 3 supports USDC but does not support CCTP. Each chain has a `maintenance_pct` of 20%, while chains 1 and 2 have `initial_pct` of 40% and chain 3 has 20%. Assume chain 1 has a balance of 100 USDC, chain 2 has 900 USDC, and chain 3 has 2000 USDC. - -The system would trigger a rebalance from chain 2 to chain 1, since the total balance of CCTP-supported chains is 1000 USDC, and chain 1 is below the 20% maintenance threshold. Chain 3 is not considered for rebalance since it does not support CCTP. - -The rebalance amount would be initially be targeted as 600 since this would take chain 2 to its 40% initial threshold. However, since chain 1 only needs 300 to reach its initial 40% threshold, the rebalance amount is clipped at 300. - -So, the final result is a rebalance of 300 USDC from chain 2 to chain 1, leading to chain 1 having 400 USDC, chain 2 having 600 USDC, and chain 3 having 2000 USDC. - - -1. At `rebalance_interval`, check the `maintenance_balance_pct` of each token on each chain and compare it to the current balance. If the balance is below the `maintenance_balance_pct`, continue -2. Calculate the amount to rebalance by taking the difference between the maintenance balance and the current balance and multiplying it by the `initial_balance_pct`. -3. If the amount to rebalance is greater than the `max_rebalance_amount`, set the amount to rebalance to the `max_rebalance_amount`. If the amount to rebalance is less than the `min_rebalance_amount`, do not rebalance. -4. Repeat after `rebalance_interval` - -The implementation for certain native bridges (e.g Scroll) is also supported. It works slightly differently as flows are only supported between Scroll and Mainnet. At a high level, the rebalancer checks inventory on Scroll versus other chains, and if imbalanced, initiates a bridge to mainnet, allowing the CCTP relayer to rebalance funds where needed. - - -### Relaying - -The relaying loop is the most complex and is responsible for relaying funds on-chain. The relayer listens to events on-chain and status updates in the database to take move transactions through the states. The states are as follows: - -1. An on-chain transaction emits the event [`BridgeRequested`](https://vercel-rfq-docs.vercel.app/contracts/interfaces/IFastBridge.sol/interface.IFastBridge.html#bridgerequested). We store this event in the db with the status [`Seen`](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq/relayer/reldb#Seen). -1. Check if the request is valid, If not, it is marked as [`WillNotProcess`](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq/relayer/reldb#WillNotProcess) -1. Check if there's enough inventory, if not mark as [`NotEnoughInventory`](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq/relayer/reldb#NotEnoughInventory) and try again later. -1. If these checks pass, it's stored as [`CommittedPending`](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq/relayer/reldb#CommittedPending). This will automatically reduce the next quote amount posted to the api since the relayers liquidity has been committed. -1. Check the chain to see if transaction is finalized yet, if not wait until it is. -1. Once the transaction is finalized on chain, update the status to [`CommitedConfirmed`](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq/relayer/reldb#CommitedConfirmed). This means the transaction is finalized on chain and we can now relay it to the destination chain. -1. Call [`relay`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#relay) on the contract to relay the transaction and update the status to [`RelayPending`](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq/relayer/reldb#RelayPending) -1. Listen for the relay in the logs. Once we get it we mark the transaction as [`RelayComplete`](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq/relayer/reldb#RelayComplete) -1. Call [`Prove()`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#prove) on the contract to prove that we relayed the transaction. Once this is done, we mark the transaction as [`ProvePosting`](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq/relayer/reldb#ProvePosting) -1. Wait for the dispute period to expire. Once it does call [`claim`](https://vercel-rfq-docs.vercel.app/contracts/FastBridge.sol/contract.FastBridge.html#claim) mark the transaction as [`ClaimPending`](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq/relayer/reldb#ClaimPending) -1. Wait for the dispute period to expire. Once it does mark the transaction as [`ClaimComplete`](https://pkg.go.dev/github.com/synapsecns/sanguine/services/rfq/relayer/reldb#ClaimComplete) - -## Running a Relayer - -### Building From Source - -To build the relayer from source, you will need to have Go installed. You can install Go by following the instructions [here](https://golang.org/doc/install). Once you have Go installed, you can build the relayer by running the following commands: - -1. `git clone https://github.com/synapsecns/sanguine --recursive` -2. `cd sanguine/services/rfq/relayer` -3. `go run main.go --config /path/to/config.yaml` - -### Running the Docker Image - -The relayer can also be run with docker. To do this, you will need to pull the [docker image](https://github.com/synapsecns/sanguine/pkgs/container/sanguine%2Frfq-relayer) and run it with the config file: - -```bash -docker run ghcr.io/synapsecns/sanguine/rfq-relayer:latest --config /path/to/config -``` - -### Withdrawals - -The `POST /withdraw` endpoint is exposed to allow for withdrawing from the relayer wallet without having to deal with the private key directly. This can be used for manual rebalancing, if desired. To use this feature, the following config values must be set: - -```yaml -enable_api_withdrawals: true -withdrawal_whitelist: - - -``` - -The relayer CLI (at `services/rfq/relayer/main.go`) exposes a withdrawal command for convenience: - -```bash -go run main.go withdraw --relayer-url https://localhost:8081 --chain-id 1 --amount 1000000000000000000 --token-address 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE --to 0x0000000000000000000000000000000000000000 -``` - -Be sure to sub in your respective `to` address! - -### Configuration - -The relayer is configured with a yaml file. The following is an example configuration: - -
- example config - ```yaml - submitter_config: # please see the more detailed submitter documentation - chains: - 1: - supports_eip_1559: true - gas_estimate: 1000000 - database: - type: sqlite # can be other mysql or sqlite - dsn: /tmp/db # should be the dsn of your database. If using sqlite, this can be a path - - signer: # please see more detailed signer config #can be text, gcp, or aws - type: GCP - file: /config/signer.txt - - screener_api_url: 'http://screener-url' # can be left blank - rfq_url: 'http://rfq-api' # url of the rfq api backend. - omnirpc_url: 'http://omnirpc' # url of the omnirpc instance, please reference the Omnirpc section under Services for proper configuration - rebalance_interval: 2m # how often to rebalance - relayer_api_port: '8081' # api port for the relayer api - volume_limit: 10000 # USD price cap for a bridge under block confirmation minimum (configurable under `chains`) - - base_chain_config: # this is hte base chain config, other chains override it - confirmations: 0 - # Claim (72.5k) + Prove (57.5k) gas limits, rounded up - origin_gas_estimate: 130_000 - # Relay gas limit, rounded up - dest_gas_estimate: 110_000 - quote_offset_bps: 2 - native_token: ETH - quote_pct: 90 - min_gas_token: 1000000000000000000 - fixed_fee_multiplier: 1.25 - - chains: - 1: - rfq_address: "0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E" # rfq contract address on eth - synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" # ccctp contract address on eth - token_messenger_address: "0xbd3fa81b58ba92a82136038b25adec7066af3155" # token messenger address on eth, note: only one of token_messenger_address or synapse_cctp_address actually needs to be present - cctp_start_block: 19341000 - confirmations: 2 - tokens: - USDC: - address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" - decimals: 6 - price_usd: 1.0 - min_quote_amount: 10000 - rebalance_method: "circlecctp" - maintenance_balance_pct: 20 - initial_balance_pct: 50 - max_rebalance_amount: 500000 - ETH: - address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" - decimals: 18 - price_usd: 2600 - 10: - rfq_address: "0x5523D3c98809DdDB82C686E152F5C58B1B0fB59E" - synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" - token_messenger_address: "0x2B4069517957735bE00ceE0fadAE88a26365528f" - cctp_start_block: 116855000 - l1_fee_chain_id: 1 - # Prove + Claim L1 gas estimate - l1_fee_origin_gas_estimate: 20 - # Relay L1 gas estimate - l1_fee_dest_gas_estimate: 10 - tokens: - USDC: - address: "0x0b2c639c533813f4aa9d7837caf62653d097ff85" - decimals: 6 - price_usd: 1.0 - min_quote_amount: 10000 - rebalance_method: "circlecctp" - maintenance_balance_pct: 20 - initial_balance_pct: 50 - max_rebalance_amount: 500000 - ETH: - address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" - decimals: 18 - price_usd: 2600 - - quotable_tokens: - 10-0x0b2c639c533813f4aa9d7837caf62653d097ff85: - - "1-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" - 1-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48: - - "10-0x0b2c639c533813f4aa9d7837caf62653d097ff85" - 1-0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE: - - "10-0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" - 10-0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE: - - "1-0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" - - fee_pricer: - gas_price_cache_ttl: 60 - token_price_cache_ttl: 60 - - cctp_relayer_config: - cctp_type: "circle" - circle_api_url: "https://iris-api.circle.com/v1/attestations" - chains: - - chain_id: 1 - synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" - token_messenger_address: "0xbd3fa81b58ba92a82136038b25adec7066af3155" - - chain_id: 10 - synapse_cctp_address: "0x12715a66773BD9C54534a01aBF01d05F6B4Bd35E" - token_messenger_address: "0x2B4069517957735bE00ceE0fadAE88a26365528f" - base_omnirpc_url: "http://omnirpc" # Make sure this is configured properly - unbonded_signer: - type: GCP - file: /config/signer.txt - http_backoff_initial_interval_ms: 1000 - http_backoff_max_elapsed_time_ms: 300000 - ``` -
- - - - - `submitter_config` - This is covered [here](../../Services/Submitter#Observability). At a high level this controls gas parameters used for on-chain transactions. - - `database` - The database settings for the API backend. A database is required to store quotes and other information. Using SQLite with a dsn set to a `/tmp/` directory is recommended for development. - - `type` - the database driver to use, can be `mysql` or `sqlite`. - - `dsn` - the dsn of your database. If using sqlite, this can be a path, if using mysql please see [here](https://dev.mysql.com/doc/connector-odbc/en/connector-odbc-configuration.html) for more information. - - `screener_api_url` (optional) - Please see [here](https://github.com/synapsecns/sanguine/tree/master/contrib/screener-api#screening-api) for an api spec, this is used descision on wether to bridge to given addresses. - - `rfq_url` - URL of the rfq api, please see the [API](../API#api-urls) page for details and the mainnet/testnet urls. - - `omnirpc_url` - URL of omnirpc to use, Please see [here](../../Services/Omnirpc) for details on running an omnirpc instance. - - `rebalance_interval` - How often to rebalance, formatted as (s = seconds, m = minutes, h = hours) - - `relayer_api_port` - the relayer api is used to control the relayer. This api should be secured/not public. - - `base_chain_config`: Base chain config is the default config applied for each chain if the other chains do not override it. This is covered in the chains section. - - `enable_guard` - Run a guard on the same instance. - - `submit_single_quotes` - Wether to use the batch endpoint for posting quotes to the api. This can be useful for debugging. - - `chains` - each chain has a different config that overrides base_chain_config. Here are the parameters for each chain - - `rfq_address` - the address of the rfq contract on this chain. These addresses are available [here](../Contracts.md). - - - `synapse_cctp_address` (optional) - this is only applicable if **rebalance_method** is set to synapse. This is the address of the CCTP contract available [here](../../CCTP/Contracts). - - `token_messenger_address` (optional) - this is only applicable if **rebalance_method** is set to cctp. Tells the relayer to use the token messenger instead of synapse. - - - `confirmations` - how many confirmations to wait before acting on an event. This will vary per-chain. - - `tokens` - this is a map of token symbol→token info for this chain. For example, token may be USDC, ETH, etc - - `address` - address of the token on this chain id - - `decimals` - number of decimals this token uses. Please verify this against the token contract itself. - - `min_quote_amount` - smallest amount to quote for a given chain. This should be balanced against expected gas spend for a relayer to be profitable. `min_quote_amount` is to be given in decimal units (so 1000.00 is 1000) - - `rebalance_method` - rebalance method for this particular kind of token. Some tokens may not have a rebalance method. This is either cctp or token messenger. - - `maintenance_balance_pct` - percent of liquidity that should be maintained on the given chain for this token. If the balance is under this amount a rebalance is triggered. - - `initial_balance_pct` - percent of liquidity to maintain after a rebalance. The total of these on all-chains should be 100. - - `min_rebalance_amount` - amount of this token to try to rebalance - - `max_rebalance_amount` - maximum amount of this token to try to rebalance at once - - `quotable_tokens`: -- `quotable_tokens`: - list of [chain-id]_[token_address]: [chain-id]_[token_address]. For example 1-0x00…. could be paired with 10-0x01 - ```yaml - "1-0x00": - - "1-0x01" - ``` -- `cctp_relayer_config`: See the [CCTP page](../../CCTP/Relayer) - -### Observability - -The RFQ relayer implements open telemetry for both tracing and metrics. Please see the [Observability](../../Observability) page for more info. There is also a custom [grafana dashboard](https://github.com/synapsecns/sanguine/tree/master/services/rfq/relayer/dashboards/dashboard.json) available for the relayer. We'd also highly recommend setting up the [submitter dashboard](../../Services/Submitter) as well. - -![Relayer Grafana Dashboard](dashboard.png) - -The metrics exposed by the relayer are: - -- `inventory_balance`: The balance of the inventory on the chain for a given `token_name` and `relayer`. -- `quote_amount`: The amount quoted for a given `token_name` and `relayer`. -- `status_count`: The distribution of non-terminal `QuoteRequestStatus` values over time. diff --git a/docs/bridge/docs/rfq/_category_.json b/docs/bridge/docs/rfq/_category_.json deleted file mode 100644 index 8097372c06..0000000000 --- a/docs/bridge/docs/rfq/_category_.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "label": "RFQ", - "position": 3, - "link": { - "type": "doc", - "id": "rfq/RFQ" - } -} diff --git a/docs/bridge/docusaurus.config.ts b/docs/bridge/docusaurus.config.ts index d7ed74fca7..9e7b597094 100644 --- a/docs/bridge/docusaurus.config.ts +++ b/docs/bridge/docusaurus.config.ts @@ -1,13 +1,32 @@ -import {themes as prismThemes} from 'prism-react-renderer'; -import type {Config} from '@docusaurus/types'; -import type * as Preset from '@docusaurus/preset-classic'; -import * as path from "path"; -import {codecovWebpackPlugin} from "@codecov/webpack-plugin"; +/* eslint-disable prefer-arrow/prefer-arrow-functions */ +import * as path from 'path' + +import { themes as prismThemes } from 'prism-react-renderer' +import type { Config } from '@docusaurus/types' +import type * as Preset from '@docusaurus/preset-classic' +import { codecovWebpackPlugin } from '@codecov/webpack-plugin' + +const options = { + id: 'api', // plugin id + docsPluginId: 'classic', // id of plugin-content-docs or preset for rendering docs + config: { + rfqapi: { + // the referenced when running CLI commands + specPath: '../../services/rfq/api/docs/swagger.yaml', // path to OpenAPI spec, URLs supported + baseUrl: 'https://rfq-api.omnirpc.io/', + outputDir: 'docs/rfq/API', // output directory for generated files + sidebarOptions: { + // optional, instructs plugin to generate sidebar.js + groupPathsBy: 'tag', // group sidebar items by operation "tag" + }, + }, + }, +} const config: Config = { title: 'Synapse Bridge Docs', tagline: 'The future is cross-chain.', - favicon: 'img/favicon.ico', + favicon: 'brand-assets/synapse-mark.svg', // Set the production url of your site here url: 'https://docs.bridge.synapseprotocol.com', @@ -41,8 +60,9 @@ const config: Config = { // Remove this to remove the "edit this page" links. editUrl: 'https://github.com/synapsecns/sanguine/tree/master/docs/bridge/', - // docLayoutComponent: "@theme/DocPage", - docItemComponent: "@theme/ApiItem" // derived from docusaurus-theme-openapi-docs + docRootComponent: '@theme/DocRoot', + docItemComponent: '@theme/ApiItem', // derived from docusaurus-theme-openapi-docs + // docItemComponent: '@theme/ApiItem', // derived from docusaurus-theme-openapi-docs }, theme: { customCss: './src/css/custom.css', @@ -58,13 +78,13 @@ const config: Config = { announcementBar: { id: 'announcementBar-v3.2', // Increment on change // content: `⭐️ If you like Docusaurus, give it a star on GitHub and follow us on Twitter ${TwitterSvg}`, - content: `⚠️️ Caution! These docs are a work in progress. Informaton may be incorrect or incomplete. For the current docs, please see here `, + content: `⚠️️ Caution! These docs are a work in progress. Information may be incorrect or incomplete. For the current docs, please see here `, }, navbar: { title: 'Synapse Bridge Docs', logo: { - alt: 'My Site Logo', - src: 'img/logo.svg', + alt: 'Synapse logo mark', + src: 'brand-assets/synapse-mark.svg', }, items: [ { @@ -75,7 +95,7 @@ const config: Config = { ], }, footer: { - style: 'dark', + // style: 'dark', links: [ // { // title: 'Docs', @@ -120,54 +140,42 @@ const config: Config = { darkTheme: prismThemes.dracula, }, } satisfies Preset.ThemeConfig, - themes: ["docusaurus-theme-openapi-docs"], // export theme components + themes: ['docusaurus-theme-openapi-docs'], // export theme components plugins: [ - [ - 'docusaurus-plugin-openapi-docs', - { - id: "api", // plugin id - docsPluginId: "classic", // id of plugin-content-docs or preset for rendering docs - config: { - rfqapi: { // the referenced when running CLI commands - specPath: "../../services/rfq/api/docs/swagger.yaml", // path to OpenAPI spec, URLs supported - baseUrl: "https://rfq-api.omnirpc.io/", - outputDir: "docs/rfq/API", // output directory for generated files - sidebarOptions: { // optional, instructs plugin to generate sidebar.js - groupPathsBy: "tag", // group sidebar items by operation "tag" - }, - }, - } - }, - ], + ['docusaurus-plugin-openapi-docs', options], // please see: https://github.com/facebook/docusaurus/issues/8091#issuecomment-1269112001 for an explanation. - () => ({ - name: 'resolve-react', - configureWebpack() { - return { - resolve: { - alias: { - // assuming root node_modules is up from "./packages/ - react: path.resolve('../../node_modules/react'), + function () { + return { + name: 'resolve-react', + configureWebpack() { + return { + resolve: { + alias: { + // assuming root node_modules is up from "./packages/ + react: path.resolve('../../node_modules/react'), + }, }, - }, - }; - }, - }), - () => ({ - name: 'bundle-analyzer', - configureWebpack() { - return { - plugins: [ - codecovWebpackPlugin({ - enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, - bundleName: "docs-bridge", - uploadToken: process.env.CODECOV_TOKEN, - }), - ] - }; + } + }, } - }), + }, + function () { + return { + name: 'bundle-analyzer', + configureWebpack() { + return { + plugins: [ + codecovWebpackPlugin({ + enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, + bundleName: 'docs-bridge', + uploadToken: process.env.CODECOV_TOKEN, + }), + ], + } + }, + } + }, ], -}; +} -export default config; +export default config satisfies Config diff --git a/docs/bridge/package.json b/docs/bridge/package.json index 078ad6c137..34caa4f315 100644 --- a/docs/bridge/package.json +++ b/docs/bridge/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/bridge-docs", - "version": "0.2.4", + "version": "0.3.0", "private": true, "scripts": { "docusaurus": "docusaurus", @@ -20,26 +20,27 @@ }, "dependencies": { "@codecov/webpack-plugin": "^0.0.1-beta.10", - "@docusaurus/core": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/plugin-content-docs": "3.1.1", - "@docusaurus/preset-classic": "3.1.1", - "@docusaurus/theme-common": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/module-type-aliases": "3.5.2", + "@docusaurus/plugin-content-docs": "3.5.2", + "@docusaurus/preset-classic": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/tsconfig": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", - "docusaurus-plugin-openapi-docs": "3.0.0-beta.10", - "docusaurus-theme-openapi-docs": "3.0.0-beta.10", + "docusaurus-plugin-openapi-docs": "^4.0.1", + "docusaurus-theme-openapi-docs": "^4.0.1", "prism-react-renderer": "^2.3.0", - "react": "^18.0.0", - "react-dom": "^18.0.0" + "react": "^18.2.0", + "react-dom": "^18.2.0", + "synapse-constants": "1.3.22" }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.2.1", - "@docusaurus/tsconfig": "3.2.1", - "@docusaurus/types": "3.2.1", "typescript": "~5.2.2" }, "browserslist": { diff --git a/docs/bridge/sidebars.ts b/docs/bridge/sidebars.ts index acc7685acd..cf181d50cd 100644 --- a/docs/bridge/sidebars.ts +++ b/docs/bridge/sidebars.ts @@ -1,18 +1,18 @@ -import type {SidebarsConfig} from '@docusaurus/plugin-content-docs'; +import type { SidebarsConfig } from '@docusaurus/plugin-content-docs' /** * Creating a sidebar enables you to: - create an ordered group of docs - render a sidebar for each doc of that group - provide next/previous navigation - + The sidebars can be generated from the filesystem, or explicitly defined here. - + Create as many sidebars as you want. */ const sidebars: SidebarsConfig = { // By default, Docusaurus generates a sidebar from the docs folder structure - tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], + tutorialSidebar: [{ type: 'autogenerated', dirName: '.' }], // But you can create a sidebar manually /* @@ -26,6 +26,6 @@ const sidebars: SidebarsConfig = { }, ], */ -}; +} -export default sidebars; +export default sidebars diff --git a/docs/bridge/src/components/AnimatedLogo.tsx b/docs/bridge/src/components/AnimatedLogo.tsx new file mode 100644 index 0000000000..61bf5dd39e --- /dev/null +++ b/docs/bridge/src/components/AnimatedLogo.tsx @@ -0,0 +1,143 @@ +export default () => { + return ( + + ) +} diff --git a/docs/bridge/src/components/BridgeFlow.tsx b/docs/bridge/src/components/BridgeFlow.tsx new file mode 100644 index 0000000000..f5e2378587 --- /dev/null +++ b/docs/bridge/src/components/BridgeFlow.tsx @@ -0,0 +1,228 @@ +export const BridgeFlow = () => { + return ( + + + + + + + + + + + + + + + + + + originChain + + + destChain + + App / SDK + Wallet + Bridge + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/docs/bridge/src/components/CCTPFlow.tsx b/docs/bridge/src/components/CCTPFlow.tsx new file mode 100644 index 0000000000..4b3daad2fb --- /dev/null +++ b/docs/bridge/src/components/CCTPFlow.tsx @@ -0,0 +1,191 @@ +export const CCTPFlow = () => { + return ( + + + + + + + + + + + + + + + + + + originChain + + + destChain + + App / SDK + Wallet + Circle + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/docs/bridge/src/components/HomepageFeatures/index.tsx b/docs/bridge/src/components/HomepageFeatures/index.tsx index 50a9e6f4c7..de3376181d 100644 --- a/docs/bridge/src/components/HomepageFeatures/index.tsx +++ b/docs/bridge/src/components/HomepageFeatures/index.tsx @@ -1,12 +1,14 @@ -import clsx from 'clsx'; -import Heading from '@theme/Heading'; -import styles from './styles.module.css'; +/* eslint-disable @typescript-eslint/no-var-requires */ +import clsx from 'clsx' +import Heading from '@theme/Heading' + +import styles from './styles.module.css' type FeatureItem = { - title: string; - Svg: React.ComponentType>; - description: JSX.Element; -}; + title: string + Svg: React.ComponentType> + description: JSX.Element +} const FeatureList: FeatureItem[] = [ { @@ -39,9 +41,9 @@ const FeatureList: FeatureItem[] = [ ), }, -]; +] -function Feature({title, Svg, description}: FeatureItem) { +const Feature = ({ title, Svg, description }: FeatureItem) => { return (
@@ -52,10 +54,10 @@ function Feature({title, Svg, description}: FeatureItem) {

{description}

- ); + ) } -export default function HomepageFeatures(): JSX.Element { +export default () => { return (
@@ -66,5 +68,5 @@ export default function HomepageFeatures(): JSX.Element {
- ); + ) } diff --git a/docs/bridge/src/components/RFQFlow.tsx b/docs/bridge/src/components/RFQFlow.tsx new file mode 100644 index 0000000000..5a25396d5d --- /dev/null +++ b/docs/bridge/src/components/RFQFlow.tsx @@ -0,0 +1,185 @@ +export const RFQFlow = () => { + return ( + + + + + + + + + + + + + + + + + + + originChain + + + destChain + + App / SDK + User + Relayer + Bridge + + + + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/docs/bridge/src/components/Routes.tsx b/docs/bridge/src/components/Routes.tsx new file mode 100644 index 0000000000..46d0cacdb0 --- /dev/null +++ b/docs/bridge/src/components/Routes.tsx @@ -0,0 +1,51 @@ +import { BRIDGABLE_TOKENS, CHAINS } from 'synapse-constants' + +const CHAINS_BY_ID = {} + +for (const { chainImg, id, name } of Object.values(CHAINS)) { + if (id && name) { + CHAINS_BY_ID[id] = { name, chainImg } + } +} + +export default () => + Object.entries(BRIDGABLE_TOKENS).map(([id, tokens]) => { + const chain = CHAINS_BY_ID[id] + const chainImg = chain.chainImg({ width: 28, height: 28 }) + return ( +
+

+ {chainImg} {chain.name} {id} +

+ {Object.values(tokens).map((token) => { + const tokenImg = + typeof token.icon === 'string' ? ( + + ) : ( + token.icon({ width: 16, height: 16 }) + ) + + return ( + + {tokenImg} {token.symbol} + + ) + })} +
+ ) + }) diff --git a/docs/bridge/src/components/SVGBridge.tsx b/docs/bridge/src/components/SVGBridge.tsx new file mode 100644 index 0000000000..9fdc0049cf --- /dev/null +++ b/docs/bridge/src/components/SVGBridge.tsx @@ -0,0 +1,182 @@ +export default () => { + return ( + + + + + + + + + + + + + + + + + Connect Wallet + + + From + + + Network + + + + + + Out + + + + 0.0000 + + + + + + To + + + Network + + + + + In + + + + 0.0000 + + + + + + Select origin token + + + + + + Bridge + + + ) +} diff --git a/docs/bridge/src/components/SVGWidget.tsx b/docs/bridge/src/components/SVGWidget.tsx new file mode 100644 index 0000000000..f674e3b8ae --- /dev/null +++ b/docs/bridge/src/components/SVGWidget.tsx @@ -0,0 +1,160 @@ +export default () => { + return ( + + + + + + + + + + + + + + + + + Network + + + + + Out + + + + Available 0.0000 + + + 0.0000 + + + + + Network + + + + 0.0000 + + + + In + + + + + + 25 seconds via SynapseRFQ + + + + + Bridge + + + ) +} diff --git a/docs/bridge/src/components/USDC.tsx b/docs/bridge/src/components/USDC.tsx new file mode 100644 index 0000000000..671a85c162 --- /dev/null +++ b/docs/bridge/src/components/USDC.tsx @@ -0,0 +1,7 @@ +import { USDC } from 'synapse-constants' +console.log(0, USDC) + +export const Test = () => { + console.log(1, USDC.icon) + return +} diff --git a/docs/bridge/src/css/custom.css b/docs/bridge/src/css/custom.css index 2bc6a4cfde..ffda217488 100644 --- a/docs/bridge/src/css/custom.css +++ b/docs/bridge/src/css/custom.css @@ -6,25 +6,107 @@ /* You can override the default Infima variables here. */ :root { - --ifm-color-primary: #2e8555; - --ifm-color-primary-dark: #29784c; - --ifm-color-primary-darker: #277148; - --ifm-color-primary-darkest: #205d3b; - --ifm-color-primary-light: #33925d; - --ifm-color-primary-lighter: #359962; - --ifm-color-primary-lightest: #3cad6e; + --ifm-color-primary: hsl(285deg 100% 35%); + --ifm-color-primary-dark: hsl(285deg 100% 32%); + --ifm-color-primary-darker: hsl(285deg 100% 30%); + --ifm-color-primary-darkest: hsl(285deg 100% 25%); + --ifm-color-primary-light: hsl(285deg 100% 39%); + --ifm-color-primary-lighter: hsl(285deg 100% 40%); + --ifm-color-primary-lightest: hsl(285deg 100% 46%); --ifm-code-font-size: 95%; - --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); + /* --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); */ + + --ifm-color-secondary-contrast-background: hsl(285deg 100% 96%); + --ifm-color-secondary-dark: hsl(285deg 100% 75%); + + --ifm-background-surface-color: #f8f8f8; + + letter-spacing: .0125em; + line-height: 1.7; } /* For readability concerns, you should choose a lighter palette in dark mode. */ -[data-theme='dark'] { - --ifm-color-primary: #25c2a0; - --ifm-color-primary-dark: #21af90; - --ifm-color-primary-darker: #1fa588; - --ifm-color-primary-darkest: #1a8870; - --ifm-color-primary-light: #29d5b0; - --ifm-color-primary-lighter: #32d8b4; - --ifm-color-primary-lightest: #4fddbf; +:root[data-theme='dark'] { + --ifm-color-primary: hsl(285deg 100% 80%); + --ifm-color-primary-dark: hsl(285deg 100% 41%); + --ifm-color-primary-darker: hsl(285deg 100% 38%); + --ifm-color-primary-darkest: hsl(285deg 100% 32%); + --ifm-color-primary-light: hsl(285deg 100% 50%); + --ifm-color-primary-lighter: hsl(285deg 100% 52%); + --ifm-color-primary-lightest: hsl(285deg 100% 59%); --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); + + --ifm-color-gray-700: hsl(240deg 20% 20%); + --ifm-color-gray-900: hsl(240deg 25% 10%); + + --ifm-background-color: hsl(240deg 25% 7.5%); + --ifm-background-surface-color: var(--ifm-color-gray-900); + --ifm-table-stripe-background: var(--ifm-color-gray-900); + + --ifm-color-secondary-contrast-background: hsl(285deg 40% 12.5%); + --ifm-color-secondary-dark: hsl(285deg 40% 25%); + + --ifm-color-success-contrast-background: hsl(165deg 40% 12.5%); + --ifm-color-success-dark: hsl(165deg 40% 25%); +} + +h1 { font-weight: 400 } + +h2 { + font-weight: 600; + border-top: 1px solid hsl(285deg 20% 50% / 20%); + margin-top: 1.5em; + padding-top: 1em; +} + +h3 { + font-size: 1.2rem; + margin: 1.5rem 0 .75rem 0; +} + +ol, ul { margin-bottom: 1.5em } +ol a, ul a { font-weight: 500 } + +table thead tr { + text-align: left; + border: none; +} + +svg.flowAnimation text { + font-family: monospace; +} + +figure { + margin: 0; + width: fit-content; +} + +figcaption { + text-align: center; + font-style: italic; + opacity: .67; + margin: 1rem 0; +} + +#flowGroup { + display: grid; + width: 100%; + gap: 2rem; + margin: 2rem 0; +} + +#flowGroup figure { + width: 100%; + max-width: 40rem; +} + +@media only screen and (min-width: 96rem) { + #flowGroup { + grid-template-columns: 1fr 1fr; + } + + #flowGroup figcaption { + /* margin: .5rem; */ + text-align: center; + } } diff --git a/docs/bridge/src/pages/index.tsx b/docs/bridge/src/pages/index.tsx index 2323a056b4..c9459a0f3a 100644 --- a/docs/bridge/src/pages/index.tsx +++ b/docs/bridge/src/pages/index.tsx @@ -1,47 +1,48 @@ -import clsx from 'clsx'; -import Link from '@docusaurus/Link'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import Layout from '@theme/Layout'; -import HomepageFeatures from '@site/src/components/HomepageFeatures'; -import Heading from '@theme/Heading'; -import { Redirect } from 'react-router-dom'; +// import clsx from 'clsx'; +// import Link from '@docusaurus/Link'; +// import useDocusaurusContext from '@docusaurus/useDocusaurusContext' +// import Layout from '@theme/Layout' +// import HomepageFeatures from '@site/src/components/HomepageFeatures' +// import Heading from '@theme/Heading' +import { Redirect } from '@docusaurus/router' -import styles from './index.module.css'; +// import styles from './index.module.css' -function HomepageHeader() { - const {siteConfig} = useDocusaurusContext(); - return ( -
-
- - {siteConfig.title} - -

{siteConfig.tagline}

-
- - Docusaurus Tutorial - 5min ⏱️ - -
-
-
- ); -} +// const HomepageHeader = () => { +// const { siteConfig } = useDocusaurusContext() +// return ( +//
+//
+// +// {siteConfig.title} +// +//

{siteConfig.tagline}

+//
+// +// Docusaurus Tutorial - 5min ⏱️ +// +//
+//
+//
+// ) +// } -export default function Home(): JSX.Element { - const {siteConfig} = useDocusaurusContext(); - // TODO: a homepage - // for now, just disable entirely: https://v1.docusaurus.io/docs/en/site-creation#docs-landing-page - return ( - - - {/**/} -
- {/**/} -
-
- ); -} +// export default () => { +// const { siteConfig } = useDocusaurusContext() +// // TODO: a homepage +// // for now, just disable entirely: https://v1.docusaurus.io/docs/en/site-creation#docs-landing-page +// return ( +// +// +// {/* +//
+// +//
*/} +//
+// ) +// } + +export default () => diff --git a/docs/bridge/static/brand-assets/synapse-border-mark.svg b/docs/bridge/static/brand-assets/synapse-border-mark.svg new file mode 100644 index 0000000000..9ca9073e8d --- /dev/null +++ b/docs/bridge/static/brand-assets/synapse-border-mark.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/bridge/static/brand-assets/synapse-brand-assets.zip b/docs/bridge/static/brand-assets/synapse-brand-assets.zip new file mode 100644 index 0000000000..1182d5ba21 Binary files /dev/null and b/docs/bridge/static/brand-assets/synapse-brand-assets.zip differ diff --git a/docs/bridge/static/brand-assets/synapse-logo-black.svg b/docs/bridge/static/brand-assets/synapse-logo-black.svg new file mode 100644 index 0000000000..75ed3028d0 --- /dev/null +++ b/docs/bridge/static/brand-assets/synapse-logo-black.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/docs/bridge/static/brand-assets/synapse-logo-onDark.svg b/docs/bridge/static/brand-assets/synapse-logo-onDark.svg new file mode 100644 index 0000000000..bcff81820e --- /dev/null +++ b/docs/bridge/static/brand-assets/synapse-logo-onDark.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/bridge/static/brand-assets/synapse-logo-onLight.svg b/docs/bridge/static/brand-assets/synapse-logo-onLight.svg new file mode 100644 index 0000000000..8e2ebc7144 --- /dev/null +++ b/docs/bridge/static/brand-assets/synapse-logo-onLight.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/bridge/static/brand-assets/synapse-logo-white.svg b/docs/bridge/static/brand-assets/synapse-logo-white.svg new file mode 100644 index 0000000000..2bae1b0d48 --- /dev/null +++ b/docs/bridge/static/brand-assets/synapse-logo-white.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/docs/bridge/static/brand-assets/synapse-mark-black.svg b/docs/bridge/static/brand-assets/synapse-mark-black.svg new file mode 100644 index 0000000000..47f379ffa1 --- /dev/null +++ b/docs/bridge/static/brand-assets/synapse-mark-black.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docs/bridge/static/brand-assets/synapse-mark-white.svg b/docs/bridge/static/brand-assets/synapse-mark-white.svg new file mode 100644 index 0000000000..f9724f7ec7 --- /dev/null +++ b/docs/bridge/static/brand-assets/synapse-mark-white.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docs/bridge/static/brand-assets/synapse-mark.svg b/docs/bridge/static/brand-assets/synapse-mark.svg new file mode 100644 index 0000000000..7d99490abc --- /dev/null +++ b/docs/bridge/static/brand-assets/synapse-mark.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/docs/bridge/static/img/docusaurus-social-card.jpg b/docs/bridge/static/img/docusaurus-social-card.jpg deleted file mode 100644 index ffcb448210..0000000000 Binary files a/docs/bridge/static/img/docusaurus-social-card.jpg and /dev/null differ diff --git a/docs/bridge/static/img/docusaurus.png b/docs/bridge/static/img/docusaurus.png deleted file mode 100644 index f458149e3c..0000000000 Binary files a/docs/bridge/static/img/docusaurus.png and /dev/null differ diff --git a/docs/bridge/static/img/favicon.ico b/docs/bridge/static/img/favicon.ico deleted file mode 100644 index a73399407a..0000000000 Binary files a/docs/bridge/static/img/favicon.ico and /dev/null differ diff --git a/docs/bridge/static/img/logo.svg b/docs/bridge/static/img/logo.svg deleted file mode 100644 index e83571b3db..0000000000 --- a/docs/bridge/static/img/logo.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/docs/bridge/static/img/undraw_docusaurus_mountain.svg b/docs/bridge/static/img/undraw_docusaurus_mountain.svg deleted file mode 100644 index af961c49a8..0000000000 --- a/docs/bridge/static/img/undraw_docusaurus_mountain.svg +++ /dev/null @@ -1,171 +0,0 @@ - - Easy to Use - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/bridge/static/img/undraw_docusaurus_react.svg b/docs/bridge/static/img/undraw_docusaurus_react.svg deleted file mode 100644 index 94b5cf08f8..0000000000 --- a/docs/bridge/static/img/undraw_docusaurus_react.svg +++ /dev/null @@ -1,170 +0,0 @@ - - Powered by React - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/bridge/static/img/undraw_docusaurus_tree.svg b/docs/bridge/static/img/undraw_docusaurus_tree.svg deleted file mode 100644 index d9161d3392..0000000000 --- a/docs/bridge/static/img/undraw_docusaurus_tree.svg +++ /dev/null @@ -1,40 +0,0 @@ - - Focus on What Matters - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/contracts-core/.vscode/settings.json b/packages/contracts-core/.vscode/settings.json new file mode 100644 index 0000000000..9cab755557 --- /dev/null +++ b/packages/contracts-core/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "[solidity]": { + "editor.defaultFormatter": "JuanBlanco.solidity" + }, + "solidity.formatter": "forge", + "solidity.monoRepoSupport": false +} diff --git a/packages/contracts-core/CHANGELOG.md b/packages/contracts-core/CHANGELOG.md index 7f396cd407..acf8984269 100644 --- a/packages/contracts-core/CHANGELOG.md +++ b/packages/contracts-core/CHANGELOG.md @@ -3,6 +3,36 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.0.34](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-core@1.0.33...@synapsecns/contracts-core@1.0.34) (2024-09-25) + + +### Bug Fixes + +* **contracts-core:** set very high gas limit for intensive tests [SLT-259] ([#3186](https://github.com/synapsecns/sanguine/issues/3186)) ([7594100](https://github.com/synapsecns/sanguine/commit/75941008756963866cb722c7c21dc81da18ac256)) + + + + + +## [1.0.33](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-core@1.0.32...@synapsecns/contracts-core@1.0.33) (2024-09-24) + + +### Bug Fixes + +* **contracts-rfq:** CI workflows [SLT-245] ([#3178](https://github.com/synapsecns/sanguine/issues/3178)) ([74b620e](https://github.com/synapsecns/sanguine/commit/74b620e4c928be8d0dbb422708376d167db7848d)) + + + + + +## [1.0.32](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-core@1.0.31...@synapsecns/contracts-core@1.0.32) (2024-09-23) + +**Note:** Version bump only for package @synapsecns/contracts-core + + + + + ## [1.0.31](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-core@1.0.30...@synapsecns/contracts-core@1.0.31) (2024-04-03) **Note:** Version bump only for package @synapsecns/contracts-core diff --git a/packages/contracts-core/foundry.toml b/packages/contracts-core/foundry.toml index f6be41d854..26d634bcd7 100644 --- a/packages/contracts-core/foundry.toml +++ b/packages/contracts-core/foundry.toml @@ -6,6 +6,8 @@ src = "contracts" out = "artifacts" libs = ["../../node_modules", "node_modules", "lib"] fs_permissions = [{ access = "read", path = "./artifacts"}, { access = "read-write", path = "./deployments"}, {access = "read-write", path = "./script"}] +# https://book.getfoundry.sh/reference/config/testing#gas_limit +gas_limit = "18446744073709551615" [profile.ci] verbosity = 4 diff --git a/packages/contracts-core/package.json b/packages/contracts-core/package.json index 495546cd2c..c8e45bfd18 100644 --- a/packages/contracts-core/package.json +++ b/packages/contracts-core/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/contracts-core", - "version": "1.0.31", + "version": "1.0.34", "description": "", "scripts": { "build": "yarn build:contracts && yarn build:typescript && yarn build:go", @@ -28,7 +28,7 @@ "@typechain/ethers-v5": "10.0.0", "hardhat": "2.22.2", "rimraf": "3.0.2", - "solhint": "3.3.8", + "solhint": "5.0.3", "typechain": "8.0.0", "typescript": "4.7.3" }, diff --git a/packages/contracts-rfq/.gitignore b/packages/contracts-rfq/.gitignore index 5726149ffb..7c3b19de11 100644 --- a/packages/contracts-rfq/.gitignore +++ b/packages/contracts-rfq/.gitignore @@ -1,3 +1,5 @@ flattened .deployments -broadcast \ No newline at end of file +broadcast + +lcov.info \ No newline at end of file diff --git a/packages/contracts-rfq/.solhint.json b/packages/contracts-rfq/.solhint.json index ce2220e0b7..2a0106eecd 100644 --- a/packages/contracts-rfq/.solhint.json +++ b/packages/contracts-rfq/.solhint.json @@ -1,3 +1,14 @@ { - "extends": "solhint:recommended" + "extends": "solhint:recommended", + "rules": { + "code-complexity": ["error"], + "func-name-mixedcase": "error", + "func-visibility": ["error", { "ignoreConstructors": true }], + "gas-custom-errors": "off", + "immutable-vars-naming": ["error", { "immutablesAsConstants": false }], + "max-line-length": ["warn", 120], + "modifier-name-mixedcase": "error", + "ordering": "warn", + "var-name-mixedcase": "error" + } } diff --git a/packages/contracts-rfq/.solhintignore b/packages/contracts-rfq/.solhintignore new file mode 100644 index 0000000000..b1133d75d8 --- /dev/null +++ b/packages/contracts-rfq/.solhintignore @@ -0,0 +1,5 @@ +contracts/FastBridge.sol +contracts/interfaces/IFastBridge.sol +script/FastBridge.s.sol +test/FastBridge.t.sol +test/FastBridgeMock.sol diff --git a/packages/contracts-rfq/.vscode/settings.json b/packages/contracts-rfq/.vscode/settings.json new file mode 100644 index 0000000000..9cab755557 --- /dev/null +++ b/packages/contracts-rfq/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "[solidity]": { + "editor.defaultFormatter": "JuanBlanco.solidity" + }, + "solidity.formatter": "forge", + "solidity.monoRepoSupport": false +} diff --git a/packages/contracts-rfq/CHANGELOG.md b/packages/contracts-rfq/CHANGELOG.md index 4c55dc04d6..16614416b6 100644 --- a/packages/contracts-rfq/CHANGELOG.md +++ b/packages/contracts-rfq/CHANGELOG.md @@ -3,6 +3,166 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.6.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.6.1...@synapsecns/contracts-rfq@0.6.2) (2024-10-03) + + +### Bug Fixes + +* **contracts-rfq:** gas bench tests for the views [SLT-275] ([#3217](https://github.com/synapsecns/sanguine/issues/3217)) ([68821a7](https://github.com/synapsecns/sanguine/commit/68821a7c7bca7b397abd7d54d5cf880af2a638b6)) + + + + + +## [0.6.1](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.6.0...@synapsecns/contracts-rfq@0.6.1) (2024-10-02) + +**Note:** Version bump only for package @synapsecns/contracts-rfq + + + + + +# [0.6.0](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.10...@synapsecns/contracts-rfq@0.6.0) (2024-10-01) + + +### Features + +* **contracts-rfq:** relayer exclusivity [SLT-187] ([#3202](https://github.com/synapsecns/sanguine/issues/3202)) ([eb3db7c](https://github.com/synapsecns/sanguine/commit/eb3db7ceb781feaa84f606869d9f9ffee5928cdf)), closes [#3204](https://github.com/synapsecns/sanguine/issues/3204) + + + + + +## [0.5.10](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.9...@synapsecns/contracts-rfq@0.5.10) (2024-10-01) + +**Note:** Version bump only for package @synapsecns/contracts-rfq + + + + + +## [0.5.9](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.8...@synapsecns/contracts-rfq@0.5.9) (2024-09-27) + +**Note:** Version bump only for package @synapsecns/contracts-rfq + + + + + +## [0.5.8](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.7...@synapsecns/contracts-rfq@0.5.8) (2024-09-27) + + +### Bug Fixes + +* **contracts-rfq:** gas estimation tests [SLT-275] ([#3204](https://github.com/synapsecns/sanguine/issues/3204)) ([0a573d1](https://github.com/synapsecns/sanguine/commit/0a573d1c1855f1682dbc1dc7722934593c2a70bd)) + + + + + +## [0.5.7](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.6...@synapsecns/contracts-rfq@0.5.7) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/contracts-rfq + + + + + +## [0.5.6](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.5...@synapsecns/contracts-rfq@0.5.6) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/contracts-rfq + + + + + +## [0.5.5](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.4...@synapsecns/contracts-rfq@0.5.5) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/contracts-rfq + + + + + +## [0.5.4](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.3...@synapsecns/contracts-rfq@0.5.4) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/contracts-rfq + + + + + +## [0.5.3](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.2...@synapsecns/contracts-rfq@0.5.3) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/contracts-rfq + + + + + +## [0.5.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.1...@synapsecns/contracts-rfq@0.5.2) (2024-09-25) + +**Note:** Version bump only for package @synapsecns/contracts-rfq + + + + + +## [0.5.1](https://github.com/synapsecns/sanguine/compare/@synapsecns/contracts-rfq@0.5.0...@synapsecns/contracts-rfq@0.5.1) (2024-09-25) + + +### Bug Fixes + +* **contracts-rfq:** limit the amount of solhint warnings [SLT-245] ([#3182](https://github.com/synapsecns/sanguine/issues/3182)) ([c15ec86](https://github.com/synapsecns/sanguine/commit/c15ec8691902817f096589553bac360b53ba40cf)) + + + + + +# 0.5.0 (2024-09-24) + + +### Bug Fixes + +* adjust to [#2658](https://github.com/synapsecns/sanguine/issues/2658) breaking changes ([#2688](https://github.com/synapsecns/sanguine/issues/2688)) ([7533f70](https://github.com/synapsecns/sanguine/commit/7533f70f4bc88f334af26a009c81c9d581d3997f)) +* **contracts-rfq:** CI workflows [SLT-245] ([#3178](https://github.com/synapsecns/sanguine/issues/3178)) ([74b620e](https://github.com/synapsecns/sanguine/commit/74b620e4c928be8d0dbb422708376d167db7848d)) + + +### Features + +* **contracts-rfq:** Deploy new RFQ Contracts ([#2255](https://github.com/synapsecns/sanguine/issues/2255)) ([b3a51e2](https://github.com/synapsecns/sanguine/commit/b3a51e28c037a93fb62fd064c3c2df5e901bd79d)) +* **contracts-rfq:** Multicall target abstraction [SLT-134] ([#3078](https://github.com/synapsecns/sanguine/issues/3078)) ([100324f](https://github.com/synapsecns/sanguine/commit/100324f269f77f73fc10913d0162676f5f918996)) +* **contracts-rfq:** relay/prove/claim with different address [SLT-130] ([#3138](https://github.com/synapsecns/sanguine/issues/3138)) ([23f6c4c](https://github.com/synapsecns/sanguine/commit/23f6c4c652743c5ca7a184ad730ce19af3600a9c)) +* **rfq:** Deploy RFQ to Base ([#2397](https://github.com/synapsecns/sanguine/issues/2397)) ([b73538e](https://github.com/synapsecns/sanguine/commit/b73538e93dfe6ecc0ce6806e75d4d1c59328c7b3)) + + +### Reverts + +* Revert "update bl" ([3693110](https://github.com/synapsecns/sanguine/commit/3693110e0f9df6177935bbbfba5444df62b11866)) + + + + + +## [0.4.1](https://github.com/synapsecns/sanguine/compare/FastBridge@0.4.0...FastBridge@0.4.1) (2024-09-23) + +**Note:** Version bump only for package FastBridge + + + + + +# [0.4.0](https://github.com/synapsecns/sanguine/compare/FastBridge@0.3.0...FastBridge@0.4.0) (2024-09-20) + + +### Features + +* **contracts-rfq:** relay/prove/claim with different address [SLT-130] ([#3138](https://github.com/synapsecns/sanguine/issues/3138)) ([23f6c4c](https://github.com/synapsecns/sanguine/commit/23f6c4c652743c5ca7a184ad730ce19af3600a9c)) + + + + + # [0.3.0](https://github.com/synapsecns/sanguine/compare/FastBridge@0.2.14...FastBridge@0.3.0) (2024-09-10) diff --git a/packages/contracts-rfq/contracts/Admin.sol b/packages/contracts-rfq/contracts/Admin.sol index 881b5bea6d..ffb352b28a 100644 --- a/packages/contracts-rfq/contracts/Admin.sol +++ b/packages/contracts-rfq/contracts/Admin.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity ^0.8.20; import {AccessControlEnumerable} from "@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol"; diff --git a/packages/contracts-rfq/contracts/FastBridgeV2.sol b/packages/contracts-rfq/contracts/FastBridgeV2.sol new file mode 100644 index 0000000000..5a764a7c95 --- /dev/null +++ b/packages/contracts-rfq/contracts/FastBridgeV2.sol @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +import {UniversalTokenLib} from "./libs/UniversalToken.sol"; + +import {Admin} from "./Admin.sol"; +import {IFastBridge} from "./interfaces/IFastBridge.sol"; +import {IFastBridgeV2} from "./interfaces/IFastBridgeV2.sol"; +import {IFastBridgeV2Errors} from "./interfaces/IFastBridgeV2Errors.sol"; + +/// @notice FastBridgeV2 is a contract for bridging tokens across chains. +contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors { + using SafeERC20 for IERC20; + using UniversalTokenLib for address; + + /// @notice Dispute period for relayed transactions + uint256 public constant DISPUTE_PERIOD = 30 minutes; + + /// @notice Delay for a transaction after which it could be permisionlessly refunded + uint256 public constant REFUND_DELAY = 7 days; + + /// @notice Minimum deadline period to relay a requested bridge transaction + uint256 public constant MIN_DEADLINE_PERIOD = 30 minutes; + + /// @notice Status of the bridge tx on origin chain + mapping(bytes32 => BridgeTxDetails) public bridgeTxDetails; + /// @notice Relay details on destination chain + mapping(bytes32 => BridgeRelay) public bridgeRelayDetails; + /// @notice Unique bridge nonces tracked per originSender + mapping(address => uint256) public senderNonces; + + /// @notice This is deprecated and should not be used. + /// @dev Replaced by senderNonces + uint256 public immutable nonce = 0; + /// @notice the block the contract was deployed at + uint256 public immutable deployBlock; + + constructor(address _owner) Admin(_owner) { + deployBlock = block.number; + } + + /// @inheritdoc IFastBridge + function bridge(BridgeParams memory params) external payable { + bridge({ + params: params, + paramsV2: BridgeParamsV2({quoteRelayer: address(0), quoteExclusivitySeconds: 0, quoteId: bytes("")}) + }); + } + + /// @inheritdoc IFastBridge + function relay(bytes memory request) external payable { + relay({request: request, relayer: msg.sender}); + } + + /// @inheritdoc IFastBridge + function prove(bytes memory request, bytes32 destTxHash) external { + prove({transactionId: keccak256(request), destTxHash: destTxHash, relayer: msg.sender}); + } + + /// @inheritdoc IFastBridgeV2 + function claim(bytes memory request) external { + claim({request: request, to: address(0)}); + } + + /// @inheritdoc IFastBridge + function dispute(bytes32 transactionId) external onlyRole(GUARD_ROLE) { + if (bridgeTxDetails[transactionId].status != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect(); + if (_timeSince(bridgeTxDetails[transactionId].proofBlockTimestamp) > DISPUTE_PERIOD) { + revert DisputePeriodPassed(); + } + + // @dev relayer gets slashed effectively if dest relay has gone thru + bridgeTxDetails[transactionId].status = BridgeStatus.REQUESTED; + bridgeTxDetails[transactionId].proofRelayer = address(0); + bridgeTxDetails[transactionId].proofBlockTimestamp = 0; + bridgeTxDetails[transactionId].proofBlockNumber = 0; + + emit BridgeProofDisputed(transactionId, msg.sender); + } + + /// @inheritdoc IFastBridge + function refund(bytes memory request) external { + bytes32 transactionId = keccak256(request); + + BridgeTransactionV2 memory transaction = getBridgeTransactionV2(request); + + if (bridgeTxDetails[transactionId].status != BridgeStatus.REQUESTED) revert StatusIncorrect(); + + if (hasRole(REFUNDER_ROLE, msg.sender)) { + // Refunder can refund if deadline has passed + if (block.timestamp <= transaction.deadline) revert DeadlineNotExceeded(); + } else { + // Permissionless refund is allowed after REFUND_DELAY + if (block.timestamp <= transaction.deadline + REFUND_DELAY) revert DeadlineNotExceeded(); + } + + // if all checks passed, set to REFUNDED status + bridgeTxDetails[transactionId].status = BridgeStatus.REFUNDED; + + // transfer origin collateral back to original sender + address to = transaction.originSender; + address token = transaction.originToken; + uint256 amount = transaction.originAmount + transaction.originFeeAmount; + token.universalTransfer(to, amount); + + emit BridgeDepositRefunded(transactionId, to, token, amount); + } + + /// @inheritdoc IFastBridge + function canClaim(bytes32 transactionId, address relayer) external view returns (bool) { + if (bridgeTxDetails[transactionId].status != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect(); + if (bridgeTxDetails[transactionId].proofRelayer != relayer) revert SenderIncorrect(); + return _timeSince(bridgeTxDetails[transactionId].proofBlockTimestamp) > DISPUTE_PERIOD; + } + + /// @inheritdoc IFastBridge + function getBridgeTransaction(bytes memory request) external pure returns (BridgeTransaction memory) { + // Note: when passing V2 request, this will decode the V1 fields correctly since the new fields were + // added as the last fields of the struct and hence the ABI decoder will simply ignore the extra data. + return abi.decode(request, (BridgeTransaction)); + } + + /// @inheritdoc IFastBridgeV2 + // TODO: reduce cyclomatic complexity alongside arbitrary call + // solhint-disable-next-line code-complexity + function bridge(BridgeParams memory params, BridgeParamsV2 memory paramsV2) public payable { + // check bridge params + if (params.dstChainId == block.chainid) revert ChainIncorrect(); + if (params.originAmount == 0 || params.destAmount == 0) revert AmountIncorrect(); + if (params.sender == address(0) || params.to == address(0)) revert ZeroAddress(); + if (params.originToken == address(0) || params.destToken == address(0)) revert ZeroAddress(); + if (params.deadline < block.timestamp + MIN_DEADLINE_PERIOD) revert DeadlineTooShort(); + int256 exclusivityEndTime = int256(block.timestamp) + paramsV2.quoteExclusivitySeconds; + // exclusivityEndTime must be in range (0 .. params.deadline] + if (exclusivityEndTime <= 0 || exclusivityEndTime > int256(params.deadline)) { + revert ExclusivityParamsIncorrect(); + } + // transfer tokens to bridge contract + // @dev use returned originAmount in request in case of transfer fees + uint256 originAmount = _pullToken(address(this), params.originToken, params.originAmount); + + // track amount of origin token owed to protocol + uint256 originFeeAmount; + if (protocolFeeRate > 0) originFeeAmount = (originAmount * protocolFeeRate) / FEE_BPS; + originAmount -= originFeeAmount; // remove from amount used in request as not relevant for relayers + + // set status to requested + bytes memory request = abi.encode( + BridgeTransactionV2({ + originChainId: uint32(block.chainid), + destChainId: params.dstChainId, + originSender: params.sender, + destRecipient: params.to, + originToken: params.originToken, + destToken: params.destToken, + originAmount: originAmount, + destAmount: params.destAmount, + originFeeAmount: originFeeAmount, + sendChainGas: params.sendChainGas, + deadline: params.deadline, + nonce: senderNonces[params.sender]++, // increment nonce on every bridge + exclusivityRelayer: paramsV2.quoteRelayer, + // We checked exclusivityEndTime to be in range (0 .. params.deadline] above, so can safely cast + exclusivityEndTime: uint256(exclusivityEndTime) + }) + ); + bytes32 transactionId = keccak256(request); + bridgeTxDetails[transactionId].status = BridgeStatus.REQUESTED; + + emit BridgeRequested( + transactionId, + params.sender, + request, + params.dstChainId, + params.originToken, + params.destToken, + originAmount, + params.destAmount, + params.sendChainGas + ); + emit BridgeQuoteDetails(transactionId, paramsV2.quoteId); + } + + /// @inheritdoc IFastBridgeV2 + // TODO: reduce cyclomatic complexity alongside arbitrary call + // solhint-disable-next-line code-complexity + function relay(bytes memory request, address relayer) public payable { + if (relayer == address(0)) revert ZeroAddress(); + // Check if the transaction has already been relayed + bytes32 transactionId = keccak256(request); + if (bridgeRelays(transactionId)) revert TransactionRelayed(); + // Decode the transaction and check that it could be relayed on this chain + BridgeTransactionV2 memory transaction = getBridgeTransactionV2(request); + if (transaction.destChainId != uint32(block.chainid)) revert ChainIncorrect(); + // Check the deadline for relay to happen + if (block.timestamp > transaction.deadline) revert DeadlineExceeded(); + // Check the exclusivity period, if it is still ongoing + // forgefmt: disable-next-item + if ( + transaction.exclusivityRelayer != address(0) && + transaction.exclusivityRelayer != relayer && + block.timestamp <= transaction.exclusivityEndTime + ) { + revert ExclusivityPeriodNotPassed(); + } + // mark bridge transaction as relayed + bridgeRelayDetails[transactionId] = + BridgeRelay({blockNumber: uint48(block.number), blockTimestamp: uint48(block.timestamp), relayer: relayer}); + + // transfer tokens to recipient on destination chain and gas rebate if requested + address to = transaction.destRecipient; + address token = transaction.destToken; + uint256 amount = transaction.destAmount; + + uint256 rebate = chainGasAmount; + if (!transaction.sendChainGas) { + // forward erc20 + rebate = 0; + _pullToken(to, token, amount); + } else if (token == UniversalTokenLib.ETH_ADDRESS) { + // lump in gas rebate into amount in native gas token + _pullToken(to, token, amount + rebate); + } else { + // forward erc20 then forward gas rebate in native gas token + _pullToken(to, token, amount); + _pullToken(to, UniversalTokenLib.ETH_ADDRESS, rebate); + } + + emit BridgeRelayed( + transactionId, + relayer, + to, + transaction.originChainId, + transaction.originToken, + transaction.destToken, + transaction.originAmount, + transaction.destAmount, + rebate + ); + } + + /// @inheritdoc IFastBridgeV2 + function prove(bytes32 transactionId, bytes32 destTxHash, address relayer) public onlyRole(RELAYER_ROLE) { + // update bridge tx status given proof provided + if (bridgeTxDetails[transactionId].status != BridgeStatus.REQUESTED) revert StatusIncorrect(); + bridgeTxDetails[transactionId].status = BridgeStatus.RELAYER_PROVED; + bridgeTxDetails[transactionId].proofBlockTimestamp = uint40(block.timestamp); + bridgeTxDetails[transactionId].proofBlockNumber = uint48(block.number); + bridgeTxDetails[transactionId].proofRelayer = relayer; + + emit BridgeProofProvided(transactionId, relayer, destTxHash); + } + + /// @inheritdoc IFastBridge + function claim(bytes memory request, address to) public { + bytes32 transactionId = keccak256(request); + BridgeTransactionV2 memory transaction = getBridgeTransactionV2(request); + + // update bridge tx status if able to claim origin collateral + if (bridgeTxDetails[transactionId].status != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect(); + + // if "to" is zero addr, permissionlessly send funds to proven relayer + if (to == address(0)) { + to = bridgeTxDetails[transactionId].proofRelayer; + } else if (bridgeTxDetails[transactionId].proofRelayer != msg.sender) { + revert SenderIncorrect(); + } + + if (_timeSince(bridgeTxDetails[transactionId].proofBlockTimestamp) <= DISPUTE_PERIOD) { + revert DisputePeriodNotPassed(); + } + + bridgeTxDetails[transactionId].status = BridgeStatus.RELAYER_CLAIMED; + + // update protocol fees if origin fee amount exists + if (transaction.originFeeAmount > 0) protocolFees[transaction.originToken] += transaction.originFeeAmount; + + // transfer origin collateral less fee to specified address + address token = transaction.originToken; + uint256 amount = transaction.originAmount; + token.universalTransfer(to, amount); + + emit BridgeDepositClaimed(transactionId, bridgeTxDetails[transactionId].proofRelayer, to, token, amount); + } + + function bridgeStatuses(bytes32 transactionId) public view returns (BridgeStatus status) { + return bridgeTxDetails[transactionId].status; + } + + function bridgeProofs(bytes32 transactionId) public view returns (uint96 timestamp, address relayer) { + timestamp = bridgeTxDetails[transactionId].proofBlockTimestamp; + relayer = bridgeTxDetails[transactionId].proofRelayer; + } + + /// @inheritdoc IFastBridgeV2 + function bridgeRelays(bytes32 transactionId) public view returns (bool) { + // has this transactionId been relayed? + return bridgeRelayDetails[transactionId].relayer != address(0); + } + + /// @inheritdoc IFastBridgeV2 + function getBridgeTransactionV2(bytes memory request) public pure returns (BridgeTransactionV2 memory) { + return abi.decode(request, (BridgeTransactionV2)); + } + + /// @notice Pulls a requested token from the user to the requested recipient. + /// @dev Be careful of re-entrancy issues when msg.value > 0 and recipient != address(this) + function _pullToken(address recipient, address token, uint256 amount) internal returns (uint256 amountPulled) { + if (token != UniversalTokenLib.ETH_ADDRESS) { + token.assertIsContract(); + // Record token balance before transfer + amountPulled = IERC20(token).balanceOf(recipient); + // Token needs to be pulled only if msg.value is zero + // This way user can specify WETH as the origin asset + IERC20(token).safeTransferFrom(msg.sender, recipient, amount); + // Use the difference between the recorded balance and the current balance as the amountPulled + amountPulled = IERC20(token).balanceOf(recipient) - amountPulled; + } else { + // Otherwise, we need to check that ETH amount matches msg.value + if (amount != msg.value) revert MsgValueIncorrect(); + // Transfer value to recipient if not this address + if (recipient != address(this)) token.universalTransfer(recipient, amount); + // We will forward msg.value in the external call later, if recipient is not this contract + amountPulled = msg.value; + } + } + + /// @notice Calculates time since proof submitted + /// @dev proof.timestamp stores casted uint40(block.timestamp) block timestamps for gas optimization + /// _timeSince(proof) can accomodate rollover case when block.timestamp > type(uint40).max but + /// proof.timestamp < type(uint40).max via unchecked statement + /// @param proofBlockTimestamp The bridge proof block timestamp + /// @return delta Time delta since proof submitted + function _timeSince(uint40 proofBlockTimestamp) internal view returns (uint256 delta) { + unchecked { + delta = uint40(block.timestamp) - proofBlockTimestamp; + } + } +} diff --git a/packages/contracts-rfq/contracts/interfaces/IAdmin.sol b/packages/contracts-rfq/contracts/interfaces/IAdmin.sol index 10d866d499..c6f398191d 100644 --- a/packages/contracts-rfq/contracts/interfaces/IAdmin.sol +++ b/packages/contracts-rfq/contracts/interfaces/IAdmin.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; interface IAdmin { // ============ Events ============ diff --git a/packages/contracts-rfq/contracts/interfaces/IFastBridge.sol b/packages/contracts-rfq/contracts/interfaces/IFastBridge.sol index 0176d557bb..b691dfb5b4 100644 --- a/packages/contracts-rfq/contracts/interfaces/IFastBridge.sol +++ b/packages/contracts-rfq/contracts/interfaces/IFastBridge.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; interface IFastBridge { struct BridgeTransaction { diff --git a/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol new file mode 100644 index 0000000000..81a67636b5 --- /dev/null +++ b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {IFastBridge} from "./IFastBridge.sol"; + +interface IFastBridgeV2 is IFastBridge { + enum BridgeStatus { + NULL, // doesn't exist yet + REQUESTED, + RELAYER_PROVED, + RELAYER_CLAIMED, + REFUNDED + } + + struct BridgeTxDetails { + BridgeStatus status; + uint40 proofBlockTimestamp; + uint48 proofBlockNumber; + address proofRelayer; + } + + struct BridgeRelay { + uint48 blockNumber; + uint48 blockTimestamp; + address relayer; + } + + /// @notice New params introduced in the FastBridgeV2. + /// We are passing fields from the older BridgeParams struct outside of this struct + /// for backwards compatibility. + /// Note: quoteRelayer and quoteExclusivitySeconds are either both zero (indicating no exclusivity) + /// or both non-zero (indicating exclusivity for the given period). + /// @param quoteRelayer Relayer that provided the quote for the transaction + /// @param quoteExclusivitySeconds Period of time the quote relayer is guaranteed exclusivity after user's deposit + /// @param quoteId Unique quote identifier used for tracking the quote + struct BridgeParamsV2 { + address quoteRelayer; + int256 quoteExclusivitySeconds; + bytes quoteId; + } + + /// @notice Updated bridge transaction struct to include parameters introduced in FastBridgeV2. + /// Note: only `exclusivityRelayer` can fill such a transaction until `exclusivityEndTime`. + /// TODO: consider changing the encoding scheme to prevent spending extra gas on decoding. + struct BridgeTransactionV2 { + uint32 originChainId; + uint32 destChainId; + address originSender; // user (origin) + address destRecipient; // user (dest) + address originToken; + address destToken; + uint256 originAmount; // amount in on origin bridge less originFeeAmount + uint256 destAmount; + uint256 originFeeAmount; + bool sendChainGas; + uint256 deadline; // user specified deadline for destination relay + uint256 nonce; + address exclusivityRelayer; + uint256 exclusivityEndTime; + } + + event BridgeQuoteDetails(bytes32 indexed transactionId, bytes quoteId); + + /// @notice Initiates bridge on origin chain to be relayed by off-chain relayer, with the ability + /// to provide temporary exclusivity fill rights for the quote relayer. + /// @param params The parameters required to bridge + /// @param paramsV2 The parameters for exclusivity fill rights (optional, could be left empty) + function bridge(BridgeParams memory params, BridgeParamsV2 memory paramsV2) external payable; + + /// @notice Relays destination side of bridge transaction by off-chain relayer + /// @param request The encoded bridge transaction to relay on destination chain + /// @param relayer The address of the relaying entity which should have control of the origin funds when claimed + function relay(bytes memory request, address relayer) external payable; + + /// @notice Provides proof on origin side that relayer provided funds on destination side of bridge transaction + /// @param transactionId The transaction id associated with the encoded bridge transaction to prove + /// @param destTxHash The destination tx hash proving bridge transaction was relayed + /// @param relayer The address of the relaying entity which should have control of the origin funds when claimed + function prove(bytes32 transactionId, bytes32 destTxHash, address relayer) external; + + /// @notice Completes bridge transaction on origin chain by claiming originally deposited capital. + /// @notice Can only send funds to the relayer address on the proof. + /// @param request The encoded bridge transaction to claim on origin chain + function claim(bytes memory request) external; + /// @notice Checks if a transaction has been relayed + /// @param transactionId The ID of the transaction to check + /// @return True if the transaction has been relayed, false otherwise + function bridgeRelays(bytes32 transactionId) external view returns (bool); + + /// @notice Returns the status of a bridge transaction + /// @param transactionId The ID of the bridge transaction + /// @return BridgeStatus Status of the bridge transaction + function bridgeStatuses(bytes32 transactionId) external view returns (BridgeStatus); + + /// @notice Returns the timestamp and relayer of a bridge proof + /// @param transactionId The ID of the bridge transaction + /// @return timestamp The timestamp of the bridge proof + /// @return relayer The relayer address of the bridge proof + function bridgeProofs(bytes32 transactionId) external view returns (uint96 timestamp, address relayer); + + /// @notice Decodes bridge request into a bridge transaction V2 struct used by FastBridgeV2 + /// @param request The bridge request to decode + function getBridgeTransactionV2(bytes memory request) external view returns (BridgeTransactionV2 memory); +} diff --git a/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2Errors.sol b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2Errors.sol new file mode 100644 index 0000000000..85f3bacdcf --- /dev/null +++ b/packages/contracts-rfq/contracts/interfaces/IFastBridgeV2Errors.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IFastBridgeV2Errors { + error AmountIncorrect(); + error ChainIncorrect(); + error ExclusivityParamsIncorrect(); + error MsgValueIncorrect(); + error SenderIncorrect(); + error StatusIncorrect(); + error ZeroAddress(); + + error DeadlineExceeded(); + error DeadlineNotExceeded(); + error DeadlineTooShort(); + error DisputePeriodNotPassed(); + error DisputePeriodPassed(); + error ExclusivityPeriodNotPassed(); + + error TransactionRelayed(); +} diff --git a/packages/contracts-rfq/contracts/interfaces/IMulticallTarget.sol b/packages/contracts-rfq/contracts/interfaces/IMulticallTarget.sol index 1f48e59609..d112057ad6 100644 --- a/packages/contracts-rfq/contracts/interfaces/IMulticallTarget.sol +++ b/packages/contracts-rfq/contracts/interfaces/IMulticallTarget.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; /// @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 diff --git a/packages/contracts-rfq/contracts/libs/Errors.sol b/packages/contracts-rfq/contracts/libs/Errors.sol index f2efb94304..b165dd46d4 100644 --- a/packages/contracts-rfq/contracts/libs/Errors.sol +++ b/packages/contracts-rfq/contracts/libs/Errors.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity ^0.8.20; error DeadlineExceeded(); error DeadlineNotExceeded(); diff --git a/packages/contracts-rfq/contracts/libs/UniversalToken.sol b/packages/contracts-rfq/contracts/libs/UniversalToken.sol index 98e6de0325..c57bf141e0 100644 --- a/packages/contracts-rfq/contracts/libs/UniversalToken.sol +++ b/packages/contracts-rfq/contracts/libs/UniversalToken.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity ^0.8.20; import {TokenNotContract} from "./Errors.sol"; diff --git a/packages/contracts-rfq/contracts/utils/MulticallTarget.sol b/packages/contracts-rfq/contracts/utils/MulticallTarget.sol index bed9266c33..d259b6c3db 100644 --- a/packages/contracts-rfq/contracts/utils/MulticallTarget.sol +++ b/packages/contracts-rfq/contracts/utils/MulticallTarget.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; import {IMulticallTarget} from "../interfaces/IMulticallTarget.sol"; diff --git a/packages/contracts-rfq/package.json b/packages/contracts-rfq/package.json index d778482a30..288df494f0 100644 --- a/packages/contracts-rfq/package.json +++ b/packages/contracts-rfq/package.json @@ -1,20 +1,18 @@ { - "name": "FastBridge", - "license": "UNLICENSED", - "version": "0.3.0", + "name": "@synapsecns/contracts-rfq", + "license": "MIT", + "version": "0.6.2", "description": "FastBridge contracts.", "private": true, "files": [ - "src/*.sol" + "contracts/**/*.sol" ], "dependencies": { "@openzeppelin/contracts": "5.0.1" }, "devDependencies": { - "@synapsecns/solidity-devops": "^0.4.4", - "prettier": "^2.5.1", - "prettier-plugin-solidity": "^1.0.0-beta.19", - "solhint": "^3.3.6" + "@synapsecns/solidity-devops": "^0.4.6", + "solhint": "5.0.3" }, "scripts": { "build": " ", @@ -22,15 +20,11 @@ "build:slither": "forge build --out=out --build-info --force", "test:coverage": "echo 'Please use foundry'", "test": "forge test", - "lint:contracts:fix": "forge fmt && solhint --fix -c .solhint.json '{contracts,script,test}/**/*.sol'", - "lint": "forge fmt && npm run prettier && npm run solhint", - "ci:lint": "yarn lint", + "lint": "forge fmt && npm run solhint", + "lint:check": "forge fmt --check && npm run solhint:check", + "ci:lint": "npm run lint:check", "build:go": "./flatten.sh contracts/*.sol test/*.sol", - "prettier": "prettier --write **.sol", - "prettier:list": "prettier --list-different **.sol", - "prettier:check": "prettier --check **.sol", - "solhint": "solhint --config ./.solhint.json 'src/**/*.sol' --fix", - "solhint:check": "solhint --config ./.solhint.json 'src/**/*.sol'", - "lint:check": "npm run prettier:check && npm run solhint:check" + "solhint": "solhint '{contracts,script,test}/**/*.sol' --fix --noPrompt --max-warnings 3", + "solhint:check": "solhint '{contracts,script,test}/**/*.sol' --max-warnings 3" } } diff --git a/packages/contracts-rfq/script/ConfigureFastBridge.s.sol b/packages/contracts-rfq/script/ConfigureFastBridge.s.sol index 4349fae615..4be61d98b1 100644 --- a/packages/contracts-rfq/script/ConfigureFastBridge.s.sol +++ b/packages/contracts-rfq/script/ConfigureFastBridge.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity ^0.8.20; import {FastBridge} from "../contracts/FastBridge.sol"; diff --git a/packages/contracts-rfq/script/DeployFastBridge.CREATE2.s.sol b/packages/contracts-rfq/script/DeployFastBridge.CREATE2.s.sol index 3369914b69..5d42a636b3 100644 --- a/packages/contracts-rfq/script/DeployFastBridge.CREATE2.s.sol +++ b/packages/contracts-rfq/script/DeployFastBridge.CREATE2.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; +pragma solidity ^0.8.20; import {FastBridge} from "../contracts/FastBridge.sol"; diff --git a/packages/contracts-rfq/test/FastBridge.t.sol b/packages/contracts-rfq/test/FastBridge.t.sol index 264ebe3b79..a571f50b40 100644 --- a/packages/contracts-rfq/test/FastBridge.t.sol +++ b/packages/contracts-rfq/test/FastBridge.t.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.13; +// solhint-disable import "forge-std/Test.sol"; import "forge-std/console2.sol"; @@ -28,15 +29,19 @@ contract FastBridgeTest is Test { MockERC20 arbUSDC; MockERC20 ethUSDC; - function setUp() public { + function setUp() public virtual { vm.chainId(42_161); - fastBridge = new FastBridge(owner); + fastBridge = FastBridge(deployFastBridge()); arbUSDC = new MockERC20("arbUSDC", 6); ethUSDC = new MockERC20("ethUSDC", 6); _mintTokensToActors(); } - function _mintTokensToActors() internal { + function deployFastBridge() internal virtual returns (address) { + return address(new FastBridge(owner)); + } + + function _mintTokensToActors() internal virtual { arbUSDC.mint(relayer, 100 * 10 ** 6); arbUSDC.mint(guard, 100 * 10 ** 6); arbUSDC.mint(user, 100 * 10 ** 6); @@ -47,6 +52,19 @@ contract FastBridgeTest is Test { ethUSDC.mint(dstUser, 100 * 10 ** 6); } + function assertCorrectProof( + bytes32 transactionId, + uint256 expectedTimestamp, + address expectedRelayer + ) + internal + virtual + { + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(transactionId); + assertEq(timestamp, uint96(expectedTimestamp)); + assertEq(relayer, expectedRelayer); + } + function _getBridgeRequestAndId( uint256 chainId, uint256 currentNonce, @@ -1297,7 +1315,7 @@ contract FastBridgeTest is Test { vm.stopPrank(); } - function test_failedRelayNotRelayer() public { + function test_failedRelayNotRelayer() public virtual { // Set up the roles for the test setUpRoles(); @@ -1345,9 +1363,7 @@ contract FastBridgeTest is Test { fastBridge.prove(request, fakeTxnHash); // We check if the bridge transaction proof timestamp is set to the timestamp at which the proof was provided - (uint96 _timestamp, address _oldRelayer) = fastBridge.bridgeProofs(transactionId); - assertEq(_timestamp, uint96(block.timestamp)); - assertEq(_oldRelayer, relayer); + assertCorrectProof(transactionId, block.timestamp, relayer); // We check if the bridge status is RELAYER_PROVED assertEq(uint256(fastBridge.bridgeStatuses(transactionId)), uint256(FastBridge.BridgeStatus.RELAYER_PROVED)); @@ -1379,9 +1395,7 @@ contract FastBridgeTest is Test { fastBridge.prove(request, fakeTxnHash); // We check if the bridge transaction proof timestamp is set to the timestamp at which the proof was provided - (uint96 _timestamp, address _oldRelayer) = fastBridge.bridgeProofs(transactionId); - assertEq(_timestamp, uint96(block.timestamp)); - assertEq(_oldRelayer, relayer); + assertCorrectProof(transactionId, block.timestamp, relayer); // We stop a prank to contain within test vm.stopPrank(); @@ -1410,9 +1424,7 @@ contract FastBridgeTest is Test { fastBridge.prove(request, fakeTxnHash); // We check if the bridge transaction proof timestamp is set to the timestamp at which the proof was provided - (uint96 _timestamp, address _oldRelayer) = fastBridge.bridgeProofs(transactionId); - assertEq(_timestamp, uint96(block.timestamp)); - assertEq(_oldRelayer, relayer); + assertCorrectProof(transactionId, block.timestamp, relayer); // We stop a prank to contain within test vm.stopPrank(); @@ -1642,7 +1654,7 @@ contract FastBridgeTest is Test { vm.stopPrank(); } - function test_failedClaimNotOldRelayer() public { + function test_failedClaimNotOldRelayer() public virtual { setUpRoles(); test_successfulBridge(); @@ -1679,7 +1691,7 @@ contract FastBridgeTest is Test { vm.stopPrank(); } - function test_failedClaimNotRelayer() public { + function test_failedClaimNotRelayer() public virtual { setUpRoles(); test_successfulRelayProof(); @@ -1709,10 +1721,8 @@ contract FastBridgeTest is Test { fastBridge.dispute(transactionId); // check status and proofs updated - (uint96 _timestamp, address _oldRelayer) = fastBridge.bridgeProofs(transactionId); assertEq(uint256(fastBridge.bridgeStatuses(transactionId)), uint256(FastBridge.BridgeStatus.REQUESTED)); - assertEq(_timestamp, 0); - assertEq(_oldRelayer, address(0)); + assertCorrectProof(transactionId, 0, address(0)); // We stop a prank to contain within test vm.stopPrank(); @@ -1735,10 +1745,8 @@ contract FastBridgeTest is Test { fastBridge.dispute(transactionId); // check status and proofs updated - (uint96 _timestamp, address _oldRelayer) = fastBridge.bridgeProofs(transactionId); assertEq(uint256(fastBridge.bridgeStatuses(transactionId)), uint256(FastBridge.BridgeStatus.REQUESTED)); - assertEq(_timestamp, 0); - assertEq(_oldRelayer, address(0)); + assertCorrectProof(transactionId, 0, address(0)); // We stop a prank to contain within test vm.stopPrank(); diff --git a/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol new file mode 100644 index 0000000000..9e79e48489 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2, FastBridgeV2Test, IFastBridgeV2} from "./FastBridgeV2.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2DstBaseTest is FastBridgeV2Test { + uint256 public constant LEFTOVER_BALANCE = 1 ether; + + function setUp() public virtual override { + vm.chainId(DST_CHAIN_ID); + super.setUp(); + } + + function deployFastBridge() public override returns (FastBridgeV2) { + return new FastBridgeV2(address(this)); + } + + function mintTokens() public virtual override { + dstToken.mint(address(relayerA), LEFTOVER_BALANCE + tokenParams.destAmount); + dstToken.mint(address(relayerB), LEFTOVER_BALANCE + tokenParams.destAmount); + deal(relayerA, LEFTOVER_BALANCE + ethParams.destAmount); + deal(relayerB, LEFTOVER_BALANCE + ethParams.destAmount); + vm.prank(relayerA); + dstToken.approve(address(fastBridge), type(uint256).max); + vm.prank(relayerB); + dstToken.approve(address(fastBridge), type(uint256).max); + } + + // ══════════════════════════════════════════════════ HELPERS ══════════════════════════════════════════════════════ + + function relay(address caller, uint256 msgValue, IFastBridgeV2.BridgeTransactionV2 memory bridgeTx) public { + bytes memory request = abi.encode(bridgeTx); + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.relay{value: msgValue}(request); + } + + function relayWithAddress( + address caller, + address relayer, + uint256 msgValue, + IFastBridgeV2.BridgeTransactionV2 memory bridgeTx + ) + public + { + bytes memory request = abi.encode(bridgeTx); + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.relay{value: msgValue}(request, relayer); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Dst.Exclusivity.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Dst.Exclusivity.t.sol new file mode 100644 index 0000000000..9d0a414306 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Dst.Exclusivity.t.sol @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2DstTest, IFastBridgeV2} from "./FastBridgeV2.Dst.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2DstExclusivityTest is FastBridgeV2DstTest { + uint256 public constant EXCLUSIVITY_PERIOD = 60 seconds; + + function createFixturesV2() public virtual override { + tokenParamsV2 = IFastBridgeV2.BridgeParamsV2({ + quoteRelayer: relayerA, + quoteExclusivitySeconds: int256(EXCLUSIVITY_PERIOD), + quoteId: "" + }); + ethParamsV2 = IFastBridgeV2.BridgeParamsV2({ + quoteRelayer: relayerB, + quoteExclusivitySeconds: int256(EXCLUSIVITY_PERIOD), + quoteId: "" + }); + + tokenTx.exclusivityRelayer = relayerA; + tokenTx.exclusivityEndTime = block.timestamp + EXCLUSIVITY_PERIOD; + ethTx.exclusivityRelayer = relayerB; + ethTx.exclusivityEndTime = block.timestamp + EXCLUSIVITY_PERIOD; + } + + // ═══════════════════════════════════════════════ RELAY: TOKEN ════════════════════════════════════════════════════ + + // Relayer A has the exclusivity fill rights within the EXCLUSIVITY_PERIOD + + function test_relay_token_exclusivityLastSecond() public { + skip(EXCLUSIVITY_PERIOD); + test_relay_token(); + } + + function test_relay_token_exclusivityOver() public { + skip(EXCLUSIVITY_PERIOD + 1); + test_relay_token(); + } + + function test_relay_token_notQuotedRelayer_revert() public { + vm.expectRevert(ExclusivityPeriodNotPassed.selector); + relay({caller: relayerB, msgValue: 0, bridgeTx: tokenTx}); + } + + function test_relay_token_notQuotedRelayer_exclusivityLastSecond_revert() public { + skip(EXCLUSIVITY_PERIOD); + vm.expectRevert(ExclusivityPeriodNotPassed.selector); + relay({caller: relayerB, msgValue: 0, bridgeTx: tokenTx}); + } + + function test_relay_token_notQuotedRelayer_exclusivityOver() public { + skip(EXCLUSIVITY_PERIOD + 1); + bytes32 txId = getTxId(tokenTx); + expectBridgeRelayed(tokenTx, txId, address(relayerB)); + relay({caller: relayerB, msgValue: 0, bridgeTx: tokenTx}); + assertTrue(fastBridge.bridgeRelays(txId)); + assertEq(dstToken.balanceOf(address(userB)), tokenParams.destAmount); + assertEq(dstToken.balanceOf(address(relayerB)), LEFTOVER_BALANCE); + assertEq(dstToken.balanceOf(address(fastBridge)), 0); + } + + function test_relay_token_withRelayerAddress_exclusivityLastSecond() public { + skip(EXCLUSIVITY_PERIOD); + test_relay_token_withRelayerAddress(); + } + + function test_relay_token_withRelayerAddress_exclusivityOver() public { + skip(EXCLUSIVITY_PERIOD + 1); + test_relay_token_withRelayerAddress(); + } + + function test_relay_token_withRelayerAddress_notQuotedRelayer_revert() public { + vm.expectRevert(ExclusivityPeriodNotPassed.selector); + relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: 0, bridgeTx: tokenTx}); + } + + function test_relay_token_withRelayerAddress_notQuotedRelayer_exclusivityLastSecond_revert() public { + skip(EXCLUSIVITY_PERIOD); + vm.expectRevert(ExclusivityPeriodNotPassed.selector); + relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: 0, bridgeTx: tokenTx}); + } + + function test_relay_token_withRelayerAddress_notQuotedRelayer_exclusivityOver() public { + skip(EXCLUSIVITY_PERIOD + 1); + bytes32 txId = getTxId(tokenTx); + expectBridgeRelayed(tokenTx, txId, address(relayerB)); + relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: 0, bridgeTx: tokenTx}); + assertTrue(fastBridge.bridgeRelays(txId)); + assertEq(dstToken.balanceOf(address(userB)), tokenParams.destAmount); + assertEq(dstToken.balanceOf(address(relayerA)), LEFTOVER_BALANCE); + assertEq(dstToken.balanceOf(address(fastBridge)), 0); + } + + // ════════════════════════════════════════════════ RELAY: ETH ═════════════════════════════════════════════════════ + + // Relayer B has the exclusivity fill rights within the EXCLUSIVITY_PERIOD + + function test_relay_eth_exclusivityLastSecond() public { + skip(EXCLUSIVITY_PERIOD); + test_relay_eth(); + } + + function test_relay_eth_exclusivityOver() public { + skip(EXCLUSIVITY_PERIOD + 1); + test_relay_eth(); + } + + function test_relay_eth_notQuotedRelayer_revert() public { + vm.expectRevert(ExclusivityPeriodNotPassed.selector); + relay({caller: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + } + + function test_relay_eth_notQuotedRelayer_exclusivityLastSecond_revert() public { + skip(EXCLUSIVITY_PERIOD); + vm.expectRevert(ExclusivityPeriodNotPassed.selector); + relay({caller: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + } + + function test_relay_eth_notQuotedRelayer_exclusivityOver() public { + skip(EXCLUSIVITY_PERIOD + 1); + bytes32 txId = getTxId(ethTx); + expectBridgeRelayed(ethTx, txId, address(relayerA)); + relay({caller: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + assertTrue(fastBridge.bridgeRelays(txId)); + assertEq(address(userB).balance, ethParams.destAmount); + assertEq(address(relayerA).balance, LEFTOVER_BALANCE); + assertEq(address(fastBridge).balance, 0); + } + + function test_relay_eth_withRelayerAddress_exclusivityLastSecond() public { + skip(EXCLUSIVITY_PERIOD); + test_relay_eth_withRelayerAddress(); + } + + function test_relay_eth_withRelayerAddress_exclusivityOver() public { + skip(EXCLUSIVITY_PERIOD + 1); + test_relay_eth_withRelayerAddress(); + } + + function test_relay_eth_withRelayerAddress_notQuotedRelayer_revert() public { + vm.expectRevert(ExclusivityPeriodNotPassed.selector); + relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + } + + function test_relay_eth_withRelayerAddress_notQuotedRelayer_exclusivityLastSecond_revert() public { + skip(EXCLUSIVITY_PERIOD); + vm.expectRevert(ExclusivityPeriodNotPassed.selector); + relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + } + + function test_relay_eth_withRelayerAddress_notQuotedRelayer_exclusivityOver() public { + skip(EXCLUSIVITY_PERIOD + 1); + bytes32 txId = getTxId(ethTx); + expectBridgeRelayed(ethTx, txId, address(relayerA)); + relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + assertTrue(fastBridge.bridgeRelays(txId)); + assertEq(address(userB).balance, ethParams.destAmount); + assertEq(address(relayerB).balance, LEFTOVER_BALANCE); + assertEq(address(fastBridge).balance, 0); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol new file mode 100644 index 0000000000..e441a9f3c4 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2DstBaseTest, IFastBridgeV2} from "./FastBridgeV2.Dst.Base.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2DstTest is FastBridgeV2DstBaseTest { + event BridgeRelayed( + bytes32 indexed transactionId, + address indexed relayer, + address indexed to, + uint32 originChainId, + address originToken, + address destToken, + uint256 originAmount, + uint256 destAmount, + uint256 chainGasAmount + ); + + function expectBridgeRelayed( + IFastBridgeV2.BridgeTransactionV2 memory bridgeTx, + bytes32 txId, + address relayer + ) + public + { + vm.expectEmit(address(fastBridge)); + emit BridgeRelayed({ + transactionId: txId, + relayer: relayer, + to: bridgeTx.destRecipient, + originChainId: bridgeTx.originChainId, + originToken: bridgeTx.originToken, + destToken: bridgeTx.destToken, + originAmount: bridgeTx.originAmount, + destAmount: bridgeTx.destAmount, + chainGasAmount: 0 + }); + } + + function checkRelayedViews(bytes32 txId, address expectedRelayer) public view { + assertTrue(fastBridge.bridgeRelays(txId)); + (uint48 blockNumber, uint48 blockTimestamp, address relayer) = fastBridge.bridgeRelayDetails(txId); + assertEq(blockNumber, block.number); + assertEq(blockTimestamp, block.timestamp); + assertEq(relayer, expectedRelayer); + } + + /// @notice RelayerA completes the ERC20 bridge request + function test_relay_token() public { + bytes32 txId = getTxId(tokenTx); + expectBridgeRelayed(tokenTx, txId, relayerA); + relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx}); + checkRelayedViews({txId: txId, expectedRelayer: relayerA}); + assertEq(dstToken.balanceOf(userB), tokenParams.destAmount); + assertEq(dstToken.balanceOf(relayerA), LEFTOVER_BALANCE); + assertEq(dstToken.balanceOf(address(fastBridge)), 0); + } + + /// @notice RelayerB completes the ERC20 bridge request, using relayerA's address + function test_relay_token_withRelayerAddress() public { + bytes32 txId = getTxId(tokenTx); + expectBridgeRelayed(tokenTx, txId, relayerA); + relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: 0, bridgeTx: tokenTx}); + checkRelayedViews({txId: txId, expectedRelayer: relayerA}); + assertEq(dstToken.balanceOf(userB), tokenParams.destAmount); + assertEq(dstToken.balanceOf(relayerB), LEFTOVER_BALANCE); + assertEq(dstToken.balanceOf(address(fastBridge)), 0); + } + + /// @notice RelayerB completes the ETH bridge request + function test_relay_eth() public { + bytes32 txId = getTxId(ethTx); + expectBridgeRelayed(ethTx, txId, relayerB); + relay({caller: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + checkRelayedViews({txId: txId, expectedRelayer: relayerB}); + assertEq(userB.balance, ethParams.destAmount); + assertEq(relayerB.balance, LEFTOVER_BALANCE); + assertEq(address(fastBridge).balance, 0); + } + + /// @notice RelayerA completes the ETH bridge request, using relayerB's address + function test_relay_eth_withRelayerAddress() public { + bytes32 txId = getTxId(ethTx); + expectBridgeRelayed(ethTx, txId, relayerB); + relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + checkRelayedViews({txId: txId, expectedRelayer: relayerB}); + assertEq(userB.balance, ethParams.destAmount); + assertEq(relayerA.balance, LEFTOVER_BALANCE); + assertEq(address(fastBridge).balance, 0); + } + + /// @notice RelayerA completes the ETH bridge request, using relayerB's address + function test_relay_eth_withRelayerAddress_checkBlockData() public { + vm.roll(987_654_321); + vm.warp(123_456_789); + bytes32 txId = getTxId(ethTx); + expectBridgeRelayed(ethTx, txId, relayerB); + relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + assertTrue(fastBridge.bridgeRelays(txId)); + (uint48 recordedBlockNumber, uint48 recordedBlockTimestamp, address recordedRelayer) = + fastBridge.bridgeRelayDetails(txId); + assertEq(recordedBlockNumber, 987_654_321); + assertEq(recordedBlockTimestamp, 123_456_789); + assertEq(recordedRelayer, relayerB); + assertEq(userB.balance, ethParams.destAmount); + assertEq(relayerA.balance, LEFTOVER_BALANCE); + assertEq(address(fastBridge).balance, 0); + } + // ══════════════════════════════════════════════════ REVERTS ══════════════════════════════════════════════════════ + + function test_relay_revert_usedRequestV1() public { + bytes memory request = abi.encode(extractV1(tokenTx)); + vm.expectRevert(); + vm.prank({msgSender: relayerA, txOrigin: relayerA}); + fastBridge.relay(request); + } + + function test_relay_revert_chainIncorrect() public { + vm.chainId(SRC_CHAIN_ID); + vm.expectRevert(ChainIncorrect.selector); + relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx}); + } + + function test_relay_revert_transactionRelayed() public { + relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx}); + vm.expectRevert(TransactionRelayed.selector); + relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx}); + } + + function test_relay_revert_deadlineExceeded() public { + skip(DEADLINE + 1); + vm.expectRevert(DeadlineExceeded.selector); + relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx}); + } + + function test_relay_withRelayerAddress_revert_usedRequestV1() public { + bytes memory request = abi.encode(extractV1(tokenTx)); + vm.expectRevert(); + vm.prank({msgSender: relayerA, txOrigin: relayerA}); + fastBridge.relay(request, relayerB); + } + + function test_relay_withRelayerAddress_revert_chainIncorrect() public { + vm.chainId(SRC_CHAIN_ID); + vm.expectRevert(ChainIncorrect.selector); + relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: 0, bridgeTx: tokenTx}); + } + + function test_relay_withRelayerAddress_revert_transactionRelayed() public { + relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx}); + vm.expectRevert(TransactionRelayed.selector); + relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: 0, bridgeTx: tokenTx}); + } + + function test_relay_withRelayerAddress_revert_deadlineExceeded() public { + skip(DEADLINE + 1); + vm.expectRevert(DeadlineExceeded.selector); + relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: 0, bridgeTx: tokenTx}); + } + + function test_relay_withRelayerAddress_revert_zeroAddr() public { + vm.expectRevert(ZeroAddress.selector); + relayWithAddress({caller: relayerA, relayer: address(0), msgValue: 0, bridgeTx: tokenTx}); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Encoding.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Encoding.t.sol new file mode 100644 index 0000000000..58123fad28 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Encoding.t.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2, FastBridgeV2Test, IFastBridge, IFastBridgeV2} from "./FastBridgeV2.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2EncodingTest is FastBridgeV2Test { + function setUp() public virtual override { + vm.chainId(SRC_CHAIN_ID); + super.setUp(); + } + + function deployFastBridge() public virtual override returns (FastBridgeV2) { + return new FastBridgeV2(address(this)); + } + + function assertEq(IFastBridge.BridgeTransaction memory a, IFastBridge.BridgeTransaction memory b) public pure { + assertEq(a.originChainId, b.originChainId); + assertEq(a.destChainId, b.destChainId); + assertEq(a.originSender, b.originSender); + assertEq(a.destRecipient, b.destRecipient); + assertEq(a.originToken, b.originToken); + assertEq(a.destToken, b.destToken); + assertEq(a.originAmount, b.originAmount); + assertEq(a.destAmount, b.destAmount); + assertEq(a.originFeeAmount, b.originFeeAmount); + assertEq(a.sendChainGas, b.sendChainGas); + assertEq(a.deadline, b.deadline); + assertEq(a.nonce, b.nonce); + } + + function assertEq( + IFastBridgeV2.BridgeTransactionV2 memory a, + IFastBridgeV2.BridgeTransactionV2 memory b + ) + public + pure + { + assertEq(extractV1(a), extractV1(b)); + assertEq(a.exclusivityRelayer, b.exclusivityRelayer); + assertEq(a.exclusivityEndTime, b.exclusivityEndTime); + } + + function test_getBridgeTransaction(IFastBridge.BridgeTransaction memory bridgeTx) public view { + bytes memory request = abi.encode(bridgeTx); + IFastBridge.BridgeTransaction memory decodedTx = fastBridge.getBridgeTransaction(request); + assertEq(decodedTx, bridgeTx); + } + + function test_getBridgeTransaction_supportsV2(IFastBridgeV2.BridgeTransactionV2 memory bridgeTxV2) public view { + bytes memory request = abi.encode(bridgeTxV2); + IFastBridge.BridgeTransaction memory decodedTx = fastBridge.getBridgeTransaction(request); + assertEq(decodedTx, extractV1(bridgeTxV2)); + } + + function test_getBridgeTransactionV2(IFastBridgeV2.BridgeTransactionV2 memory bridgeTxV2) public view { + bytes memory request = abi.encode(bridgeTxV2); + IFastBridgeV2.BridgeTransactionV2 memory decodedTxV2 = fastBridge.getBridgeTransactionV2(request); + assertEq(decodedTxV2, bridgeTxV2); + } + + function test_getBridgeTransactionV2_revert_usedRequestV1(IFastBridge.BridgeTransaction memory bridgeTx) public { + bytes memory request = abi.encode(bridgeTx); + vm.expectRevert(); + fastBridge.getBridgeTransactionV2(request); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.Excl.t.sol b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.Excl.t.sol new file mode 100644 index 0000000000..dc6b5953f5 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.Excl.t.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2DstGasBenchmarkTest} from "./FastBridgeV2.GasBench.Dst.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2DstExclusivityTest is FastBridgeV2DstGasBenchmarkTest { + uint256 public constant EXCLUSIVITY_PERIOD = 60 seconds; + + function setUp() public virtual override { + super.setUp(); + skip({time: EXCLUSIVITY_PERIOD / 2}); + } + + function createFixturesV2() public virtual override { + tokenParamsV2.quoteRelayer = relayerA; + tokenParamsV2.quoteExclusivitySeconds = int256(EXCLUSIVITY_PERIOD); + ethParamsV2.quoteRelayer = relayerA; + ethParamsV2.quoteExclusivitySeconds = int256(EXCLUSIVITY_PERIOD); + tokenTx.exclusivityRelayer = relayerA; + tokenTx.exclusivityEndTime = block.timestamp + EXCLUSIVITY_PERIOD; + ethTx.exclusivityRelayer = relayerA; + ethTx.exclusivityEndTime = block.timestamp + EXCLUSIVITY_PERIOD; + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.t.sol b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.t.sol new file mode 100644 index 0000000000..6afabce02e --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.t.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2DstBaseTest} from "./FastBridgeV2.Dst.Base.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +/// @notice This test is used to estimate the gas cost of FastBridgeV2 destination chain functions. +/// Very little state checks are performed, make sure to do full coverage in different tests. +contract FastBridgeV2DstGasBenchmarkTest is FastBridgeV2DstBaseTest { + uint256 public constant INITIAL_USER_BALANCE = 100 ether; + + function mintTokens() public virtual override { + super.mintTokens(); + deal(userB, INITIAL_USER_BALANCE); + dstToken.mint(userB, INITIAL_USER_BALANCE); + } + + // ═══════════════════════════════════════════════════ TOKEN ═══════════════════════════════════════════════════════ + + function test_relay_token() public { + bytes32 txId = getTxId(tokenTx); + relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx}); + (uint256 blockNumber, uint256 blockTimestamp, address relayer) = fastBridge.bridgeRelayDetails(txId); + assertEq(blockNumber, block.number); + assertEq(blockTimestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(dstToken.balanceOf(userB), INITIAL_USER_BALANCE + tokenParams.destAmount); + assertEq(dstToken.balanceOf(relayerA), LEFTOVER_BALANCE); + } + + function test_relay_token_withRelayerAddress() public { + bytes32 txId = getTxId(tokenTx); + relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: 0, bridgeTx: tokenTx}); + (uint256 blockNumber, uint256 blockTimestamp, address relayer) = fastBridge.bridgeRelayDetails(txId); + assertEq(blockNumber, block.number); + assertEq(blockTimestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(dstToken.balanceOf(userB), INITIAL_USER_BALANCE + tokenParams.destAmount); + assertEq(dstToken.balanceOf(relayerB), LEFTOVER_BALANCE); + } + + // ════════════════════════════════════════════════════ ETH ════════════════════════════════════════════════════════ + + function test_relay_eth() public { + bytes32 txId = getTxId(ethTx); + relay({caller: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + (uint256 blockNumber, uint256 blockTimestamp, address relayer) = fastBridge.bridgeRelayDetails(txId); + assertEq(blockNumber, block.number); + assertEq(blockTimestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(address(userB).balance, INITIAL_USER_BALANCE + ethParams.destAmount); + assertEq(address(relayerA).balance, LEFTOVER_BALANCE); + } + + function test_relay_eth_withRelayerAddress() public { + bytes32 txId = getTxId(ethTx); + relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + (uint256 blockNumber, uint256 blockTimestamp, address relayer) = fastBridge.bridgeRelayDetails(txId); + assertEq(blockNumber, block.number); + assertEq(blockTimestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(address(userB).balance, INITIAL_USER_BALANCE + ethParams.destAmount); + assertEq(address(relayerB).balance, LEFTOVER_BALANCE); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Encoding.t.sol b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Encoding.t.sol new file mode 100644 index 0000000000..703e73267b --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Encoding.t.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2SrcBaseTest} from "./FastBridgeV2.Src.Base.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2GasBenchmarkSrcProtocolFeesTest is FastBridgeV2SrcBaseTest { + // TODO: add more tests with variable length requests once arbitrary call is done + + function test_getBridgeTransaction() public view { + bytes memory request = abi.encode(extractV1(tokenTx)); + fastBridge.getBridgeTransaction(request); + } + + function test_getBridgeTransactionV2() public view { + bytes memory request = abi.encode(tokenTx); + fastBridge.getBridgeTransactionV2(request); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.PFees.t.sol b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.PFees.t.sol new file mode 100644 index 0000000000..77b439d18e --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.PFees.t.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2GasBenchmarkSrcTest} from "./FastBridgeV2.GasBench.Src.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2GasBenchmarkSrcProtocolFeesTest is FastBridgeV2GasBenchmarkSrcTest { + function configureFastBridge() public virtual override { + super.configureFastBridge(); + fastBridge.grantRole(fastBridge.GOVERNOR_ROLE(), address(this)); + fastBridge.setProtocolFeeRate(1e4); // 1% + } + + function createFixtures() public virtual override { + super.createFixtures(); + tokenTx.originFeeAmount = 0.01e6; + tokenTx.originAmount = 0.99e6; + tokenTx.destAmount = 0.98e6; + tokenParams.destAmount = 0.98e6; + ethTx.originFeeAmount = 0.01 ether; + ethTx.originAmount = 0.99 ether; + ethTx.destAmount = 0.98 ether; + ethParams.destAmount = 0.98 ether; + + // Copy txs to bridged and proven with different nonce + bridgedTokenTx = tokenTx; + provenTokenTx = tokenTx; + bridgedEthTx = ethTx; + provenEthTx = ethTx; + + bridgedTokenTx.nonce = 0; + bridgedEthTx.nonce = 1; + provenTokenTx.nonce = 2; + provenEthTx.nonce = 3; + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol new file mode 100644 index 0000000000..0d6314baac --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2SrcBaseTest, IFastBridgeV2} from "./FastBridgeV2.Src.Base.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +/// @notice This test is used to estimate the gas cost of FastBridgeV2 source chain functions. +/// Very little state checks are performed, make sure to do full coverage in different tests. +contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { + uint256 public constant BLOCK_TIME = 12 seconds; + uint256 public constant INITIAL_RELAYER_BALANCE = 100 ether; + uint256 public constant EXCLUSIVITY_PERIOD = 60 seconds; + + IFastBridgeV2.BridgeTransactionV2 internal bridgedTokenTx; + IFastBridgeV2.BridgeTransactionV2 internal bridgedEthTx; + + IFastBridgeV2.BridgeTransactionV2 internal provenTokenTx; + IFastBridgeV2.BridgeTransactionV2 internal provenEthTx; + + uint256 public initialUserBalanceToken; + uint256 public initialUserBalanceEth; + uint256 public initialFastBridgeBalanceToken; + uint256 public initialFastBridgeBalanceEth; + + function setUp() public virtual override { + super.setUp(); + initExistingTxs(); + initialUserBalanceToken = srcToken.balanceOf(userA); + initialUserBalanceEth = userA.balance; + initialFastBridgeBalanceToken = srcToken.balanceOf(address(fastBridge)); + initialFastBridgeBalanceEth = address(fastBridge).balance; + } + + function createFixtures() public virtual override { + super.createFixtures(); + bridgedTokenTx = tokenTx; + provenTokenTx = tokenTx; + bridgedEthTx = ethTx; + provenEthTx = ethTx; + + bridgedTokenTx.nonce = 0; + bridgedEthTx.nonce = 1; + provenTokenTx.nonce = 2; + provenEthTx.nonce = 3; + // Next nonce for userA tx would be 4 (either token or eth) + tokenTx.nonce = 4; + ethTx.nonce = 4; + } + + function createFixturesV2() public virtual override { + super.createFixturesV2(); + bridgedTokenTx.exclusivityEndTime = block.timestamp; + provenTokenTx.exclusivityEndTime = block.timestamp; + bridgedEthTx.exclusivityEndTime = block.timestamp; + provenEthTx.exclusivityEndTime = block.timestamp; + // Actual tx will be submitted one block later + tokenTx.exclusivityEndTime = block.timestamp + BLOCK_TIME; + ethTx.exclusivityEndTime = block.timestamp + BLOCK_TIME; + } + + function mintTokens() public virtual override { + super.mintTokens(); + srcToken.mint(relayerA, INITIAL_RELAYER_BALANCE); + srcToken.mint(relayerB, INITIAL_RELAYER_BALANCE); + deal(relayerA, INITIAL_RELAYER_BALANCE); + deal(relayerB, INITIAL_RELAYER_BALANCE); + } + + function initExistingTxs() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + skipBlocksExactly(1); + prove({caller: relayerA, bridgeTx: provenTokenTx, destTxHash: hex"01"}); + prove({caller: relayerB, transactionId: getTxId(provenEthTx), destTxHash: hex"02", relayer: relayerA}); + // Status checks + assertEq(fastBridge.bridgeStatuses(getTxId(bridgedTokenTx)), IFastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(getTxId(bridgedEthTx)), IFastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(getTxId(provenTokenTx)), IFastBridgeV2.BridgeStatus.RELAYER_PROVED); + assertEq(fastBridge.bridgeStatuses(getTxId(provenEthTx)), IFastBridgeV2.BridgeStatus.RELAYER_PROVED); + assertEq(fastBridge.bridgeStatuses(getTxId(tokenTx)), IFastBridgeV2.BridgeStatus.NULL); + assertEq(fastBridge.bridgeStatuses(getTxId(ethTx)), IFastBridgeV2.BridgeStatus.NULL); + } + + function skipBlocksExactly(uint256 blocks) public { + vm.roll(block.number + blocks); + vm.warp(block.timestamp + blocks * BLOCK_TIME); + } + + function skipTimeAtLeast(uint256 time) public { + uint256 blocksToSkip = time / BLOCK_TIME; + if (blocksToSkip * BLOCK_TIME < time) blocksToSkip++; + skipBlocksExactly(blocksToSkip); + } + + // ═══════════════════════════════════════════════════ TOKEN ═══════════════════════════════════════════════════════ + + function test_bridge_token() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + assertEq(fastBridge.bridgeStatuses(getTxId(tokenTx)), IFastBridgeV2.BridgeStatus.REQUESTED); + assertEq(srcToken.balanceOf(userA), initialUserBalanceToken - tokenParams.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken + tokenParams.originAmount); + } + + function test_bridge_token_withExclusivity() public { + tokenParamsV2.quoteRelayer = relayerA; + tokenParamsV2.quoteExclusivitySeconds = int256(EXCLUSIVITY_PERIOD); + tokenParamsV2.quoteId = bytes("Created by Relayer A"); + tokenTx.exclusivityRelayer = relayerA; + tokenTx.exclusivityEndTime = block.timestamp + EXCLUSIVITY_PERIOD; + bridge({caller: userA, msgValue: 0, params: tokenParams, paramsV2: tokenParamsV2}); + assertEq(fastBridge.bridgeStatuses(getTxId(tokenTx)), IFastBridgeV2.BridgeStatus.REQUESTED); + assertEq(srcToken.balanceOf(userA), initialUserBalanceToken - tokenParams.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken + tokenParams.originAmount); + } + + function test_prove_token() public { + bytes32 txId = getTxId(bridgedTokenTx); + prove({caller: relayerA, bridgeTx: bridgedTokenTx, destTxHash: hex"03"}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_PROVED); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken); + } + + function test_proveWithAddress_token() public { + bytes32 txId = getTxId(bridgedTokenTx); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"03", relayer: relayerA}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_PROVED); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + } + + function test_claim_token() public { + bytes32 txId = getTxId(provenTokenTx); + skipTimeAtLeast({time: CLAIM_DELAY + 1}); + assertTrue(fastBridge.canClaim(txId, relayerA)); + claim({caller: relayerA, bridgeTx: provenTokenTx}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(srcToken.balanceOf(relayerA), INITIAL_RELAYER_BALANCE + tokenTx.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenTx.originAmount); + } + + function test_claimWithAddress_token() public { + bytes32 txId = getTxId(provenTokenTx); + skipTimeAtLeast({time: CLAIM_DELAY + 1}); + assertTrue(fastBridge.canClaim(txId, relayerA)); + claim({caller: relayerA, bridgeTx: provenTokenTx, to: relayerB}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(srcToken.balanceOf(relayerB), INITIAL_RELAYER_BALANCE + tokenTx.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenTx.originAmount); + } + + function test_dispute_token() public { + bytes32 txId = getTxId(provenTokenTx); + dispute({caller: guard, txId: txId}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken); + } + + function test_refundPermissioned_token() public { + bytes32 txId = getTxId(bridgedTokenTx); + skipTimeAtLeast({time: DEADLINE}); + refund({caller: refunder, bridgeTx: bridgedTokenTx}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); + assertEq(srcToken.balanceOf(userA), initialUserBalanceToken + tokenParams.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenParams.originAmount); + } + + function test_refundPermissionless_token() public { + bytes32 txId = getTxId(bridgedTokenTx); + skipTimeAtLeast({time: DEADLINE + PERMISSIONLESS_REFUND_DELAY}); + refund({caller: userB, bridgeTx: bridgedTokenTx}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); + assertEq(srcToken.balanceOf(userA), initialUserBalanceToken + tokenParams.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenParams.originAmount); + } + + // ════════════════════════════════════════════════════ ETH ════════════════════════════════════════════════════════ + + function test_bridge_eth() public { + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + assertEq(fastBridge.bridgeStatuses(getTxId(ethTx)), IFastBridgeV2.BridgeStatus.REQUESTED); + assertEq(userA.balance, initialUserBalanceEth - ethParams.originAmount); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth + ethParams.originAmount); + } + + function test_bridge_eth_withExclusivity() public { + ethParamsV2.quoteRelayer = relayerA; + ethParamsV2.quoteExclusivitySeconds = int256(EXCLUSIVITY_PERIOD); + ethParamsV2.quoteId = bytes("Created by Relayer A"); + ethTx.exclusivityRelayer = relayerA; + ethTx.exclusivityEndTime = block.timestamp + EXCLUSIVITY_PERIOD; + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams, paramsV2: ethParamsV2}); + assertEq(fastBridge.bridgeStatuses(getTxId(ethTx)), IFastBridgeV2.BridgeStatus.REQUESTED); + assertEq(userA.balance, initialUserBalanceEth - ethParams.originAmount); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth + ethParams.originAmount); + } + + function test_prove_eth() public { + bytes32 txId = getTxId(bridgedEthTx); + prove({caller: relayerA, bridgeTx: bridgedEthTx, destTxHash: hex"03"}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_PROVED); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth); + } + + function test_proveWithAddress_eth() public { + bytes32 txId = getTxId(bridgedEthTx); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"03", relayer: relayerA}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_PROVED); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth); + } + + function test_claim_eth() public { + bytes32 txId = getTxId(provenEthTx); + skipTimeAtLeast({time: CLAIM_DELAY + 1}); + assertTrue(fastBridge.canClaim(txId, relayerA)); + claim({caller: relayerA, bridgeTx: provenEthTx}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(relayerA.balance, INITIAL_RELAYER_BALANCE + ethTx.originAmount); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethTx.originAmount); + } + + function test_claimWithAddress_eth() public { + bytes32 txId = getTxId(provenEthTx); + skipTimeAtLeast({time: CLAIM_DELAY + 1}); + assertTrue(fastBridge.canClaim(txId, relayerA)); + claim({caller: relayerA, bridgeTx: provenEthTx, to: relayerB}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(relayerB.balance, INITIAL_RELAYER_BALANCE + ethTx.originAmount); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethTx.originAmount); + } + + function test_dispute_eth() public { + bytes32 txId = getTxId(provenEthTx); + dispute({caller: guard, txId: txId}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth); + } + + function test_refundPermissioned_eth() public { + bytes32 txId = getTxId(bridgedEthTx); + skipTimeAtLeast({time: DEADLINE}); + refund({caller: refunder, bridgeTx: bridgedEthTx}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); + assertEq(userA.balance, initialUserBalanceEth + ethParams.originAmount); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethParams.originAmount); + } + + function test_refundPermissionless_eth() public { + bytes32 txId = getTxId(bridgedEthTx); + skipTimeAtLeast({time: DEADLINE + PERMISSIONLESS_REFUND_DELAY}); + refund({caller: userB, bridgeTx: bridgedEthTx}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); + assertEq(userA.balance, initialUserBalanceEth + ethParams.originAmount); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethParams.originAmount); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Management.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Management.t.sol new file mode 100644 index 0000000000..58502618b9 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Management.t.sol @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2, FastBridgeV2Test} from "./FastBridgeV2.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2ManagementTest is FastBridgeV2Test { + uint256 public constant FEE_RATE_MAX = 1e4; // 1% + bytes32 public constant GOVERNOR_ROLE = keccak256("GOVERNOR_ROLE"); + + address public admin = makeAddr("Admin"); + address public governorA = makeAddr("Governor A"); + + event FeeRateUpdated(uint256 oldFeeRate, uint256 newFeeRate); + event FeesSwept(address token, address recipient, uint256 amount); + event ChainGasAmountUpdated(uint256 oldChainGasAmount, uint256 newChainGasAmount); + + function deployFastBridge() public override returns (FastBridgeV2) { + return new FastBridgeV2(admin); + } + + function configureFastBridge() public override { + setGovernor(admin, governor); + } + + function mintTokens() public override { + srcToken.mint(address(fastBridge), 100); + deal(address(fastBridge), 200); + cheatCollectedProtocolFees(address(srcToken), 100); + cheatCollectedProtocolFees(ETH_ADDRESS, 200); + } + + function setGovernor(address caller, address newGovernor) public { + vm.prank(caller); + fastBridge.grantRole(GOVERNOR_ROLE, newGovernor); + } + + function setProtocolFeeRate(address caller, uint256 newFeeRate) public { + vm.prank(caller); + fastBridge.setProtocolFeeRate(newFeeRate); + } + + function sweepProtocolFees(address caller, address token, address recipient) public { + vm.prank(caller); + fastBridge.sweepProtocolFees(token, recipient); + } + + function setChainGasAmount(address caller, uint256 newChainGasAmount) public { + vm.prank(caller); + fastBridge.setChainGasAmount(newChainGasAmount); + } + + function test_grantGovernorRole() public { + assertFalse(fastBridge.hasRole(GOVERNOR_ROLE, governorA)); + setGovernor(admin, governorA); + assertTrue(fastBridge.hasRole(GOVERNOR_ROLE, governorA)); + } + + function test_grantGovernorRole_revertNotAdmin(address caller) public { + vm.assume(caller != admin); + expectUnauthorized(caller, fastBridge.DEFAULT_ADMIN_ROLE()); + setGovernor(caller, governorA); + } + + // ═══════════════════════════════════════════ SET PROTOCOL FEE RATE ═══════════════════════════════════════════════ + + function test_setProtocolFeeRate() public { + vm.expectEmit(address(fastBridge)); + emit FeeRateUpdated(0, 123); + setProtocolFeeRate(governor, 123); + assertEq(fastBridge.protocolFeeRate(), 123); + } + + function test_setProtocolFeeRate_twice() public { + test_setProtocolFeeRate(); + vm.expectEmit(address(fastBridge)); + emit FeeRateUpdated(123, FEE_RATE_MAX); + setProtocolFeeRate(governor, FEE_RATE_MAX); + assertEq(fastBridge.protocolFeeRate(), FEE_RATE_MAX); + } + + function test_setProtocolFeeRate_revert_tooHigh() public { + vm.expectRevert("newFeeRate > max"); + setProtocolFeeRate(governor, FEE_RATE_MAX + 1); + } + + function test_setProtocolFeeRate_revert_notGovernor(address caller) public { + vm.assume(caller != governor); + expectUnauthorized(caller, fastBridge.GOVERNOR_ROLE()); + setProtocolFeeRate(caller, 123); + } + + // ════════════════════════════════════════════ SWEEP PROTOCOL FEES ════════════════════════════════════════════════ + + function test_sweepProtocolFees_erc20() public { + vm.expectEmit(address(fastBridge)); + emit FeesSwept(address(srcToken), governorA, 100); + sweepProtocolFees(governor, address(srcToken), governorA); + assertEq(srcToken.balanceOf(address(fastBridge)), 0); + assertEq(srcToken.balanceOf(governorA), 100); + assertEq(fastBridge.protocolFees(address(srcToken)), 0); + } + + function test_sweepProtocolFees_eth() public { + vm.expectEmit(address(fastBridge)); + emit FeesSwept(ETH_ADDRESS, governorA, 200); + sweepProtocolFees(governor, ETH_ADDRESS, governorA); + assertEq(address(fastBridge).balance, 0); + assertEq(governorA.balance, 200); + assertEq(fastBridge.protocolFees(ETH_ADDRESS), 0); + } + + function test_sweepProtocolFees_revertNotGovernor(address caller) public { + vm.assume(caller != governor); + expectUnauthorized(caller, fastBridge.GOVERNOR_ROLE()); + sweepProtocolFees(caller, address(srcToken), governorA); + } + + // ═══════════════════════════════════════════ SET CHAIN GAS AMOUNT ════════════════════════════════════════════════ + + function test_setChainGasAmount() public { + vm.expectEmit(address(fastBridge)); + emit ChainGasAmountUpdated(0, 123); + setChainGasAmount(governor, 123); + assertEq(fastBridge.chainGasAmount(), 123); + } + + function test_setChainGasAmount_twice() public { + test_setChainGasAmount(); + vm.expectEmit(address(fastBridge)); + emit ChainGasAmountUpdated(123, 456); + setChainGasAmount(governor, 456); + assertEq(fastBridge.chainGasAmount(), 456); + } + + function test_setChainGasAmount_revertNotGovernor(address caller) public { + vm.assume(caller != governor); + expectUnauthorized(caller, fastBridge.GOVERNOR_ROLE()); + setChainGasAmount(caller, 123); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Parity.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Parity.t.sol new file mode 100644 index 0000000000..081913d7eb --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Parity.t.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +import {IFastBridgeV2Errors} from "../contracts/interfaces/IFastBridgeV2Errors.sol"; + +import {FastBridgeTest} from "./FastBridge.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +/// @notice Contract was updated to be abstract to prevent these tests from being run, +/// as the FastBridgeV2 contract is no longer fully backwards compatible with FastBridge. +abstract contract FastBridgeV2ParityTest is FastBridgeTest, IFastBridgeV2Errors { + address public anotherRelayer = makeAddr("Another Relayer"); + + function deployFastBridge() internal virtual override returns (address) { + // Use the cheatcode to deploy 0.8.24 contract within a 0.8.20 test + return deployCode({what: "FastBridgeV2", args: abi.encode(owner)}); + } + + /// @notice We use uint40 for the timestamps in FastBridgeV2 + function assertCorrectProof( + bytes32 transactionId, + uint256 expectedTimestamp, + address expectedRelayer + ) + internal + virtual + override + { + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(transactionId); + assertEq(timestamp, uint40(expectedTimestamp)); + assertEq(relayer, expectedRelayer); + } + + /// @notice Relay function is no longer permissioned, so we skip this test + function test_failedRelayNotRelayer() public virtual override { + vm.skip(true); + } + + /// @notice Claim function is no longer permissioned by the role (but still by proven address), + /// so we skip this test + function test_failedClaimNotRelayer() public virtual override { + vm.skip(true); + } + + /// @notice Claim function is no longer permissioned by the role (but still by proven address), + /// so we modify the parent test by removing the role assignment. + function test_failedClaimNotOldRelayer() public virtual override { + setUpRoles(); + test_successfulBridge(); + (bytes memory request,) = _getBridgeRequestAndId(block.chainid, 0, 0); + vm.warp(block.timestamp + 31 minutes); + vm.prank(relayer); + fastBridge.prove(request, bytes32("0x04")); + + vm.expectRevert(abi.encodeWithSelector(SenderIncorrect.selector)); + vm.prank(anotherRelayer); + fastBridge.claim(request, relayer); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol new file mode 100644 index 0000000000..e63f292194 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2, FastBridgeV2Test, IFastBridge, IFastBridgeV2} from "./FastBridgeV2.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +abstract contract FastBridgeV2SrcBaseTest is FastBridgeV2Test { + uint256 public constant MIN_DEADLINE = 30 minutes; + uint256 public constant CLAIM_DELAY = 30 minutes; + uint256 public constant PERMISSIONLESS_REFUND_DELAY = 7 days; + + uint256 public constant LEFTOVER_BALANCE = 10 ether; + uint256 public constant INITIAL_PROTOCOL_FEES_TOKEN = 456_789; + uint256 public constant INITIAL_PROTOCOL_FEES_ETH = 0.123 ether; + + function setUp() public virtual override { + vm.chainId(SRC_CHAIN_ID); + super.setUp(); + } + + function deployFastBridge() public virtual override returns (FastBridgeV2) { + return new FastBridgeV2(address(this)); + } + + function configureFastBridge() public virtual override { + fastBridge.grantRole(fastBridge.RELAYER_ROLE(), relayerA); + fastBridge.grantRole(fastBridge.RELAYER_ROLE(), relayerB); + fastBridge.grantRole(fastBridge.GUARD_ROLE(), guard); + fastBridge.grantRole(fastBridge.REFUNDER_ROLE(), refunder); + } + + function mintTokens() public virtual override { + // Prior Protocol fees + srcToken.mint(address(fastBridge), INITIAL_PROTOCOL_FEES_TOKEN); + deal(address(fastBridge), INITIAL_PROTOCOL_FEES_ETH); + cheatCollectedProtocolFees(address(srcToken), INITIAL_PROTOCOL_FEES_TOKEN); + cheatCollectedProtocolFees(ETH_ADDRESS, INITIAL_PROTOCOL_FEES_ETH); + // Users + srcToken.mint(userA, LEFTOVER_BALANCE + tokenParams.originAmount); + srcToken.mint(userB, LEFTOVER_BALANCE + tokenParams.originAmount); + deal(userA, LEFTOVER_BALANCE + ethParams.originAmount); + deal(userB, LEFTOVER_BALANCE + ethParams.originAmount); + vm.prank(userA); + srcToken.approve(address(fastBridge), type(uint256).max); + vm.prank(userB); + srcToken.approve(address(fastBridge), type(uint256).max); + } + + // ══════════════════════════════════════════════════ HELPERS ══════════════════════════════════════════════════════ + + function bridge(address caller, uint256 msgValue, IFastBridge.BridgeParams memory params) public virtual { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.bridge{value: msgValue}(params); + } + + function bridge( + address caller, + uint256 msgValue, + IFastBridge.BridgeParams memory params, + IFastBridgeV2.BridgeParamsV2 memory paramsV2 + ) + public + { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.bridge{value: msgValue}(params, paramsV2); + } + + function prove(address caller, bytes32 transactionId, bytes32 destTxHash, address relayer) public { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.prove(transactionId, destTxHash, relayer); + } + + function prove(address caller, IFastBridgeV2.BridgeTransactionV2 memory bridgeTx, bytes32 destTxHash) public { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.prove(abi.encode(bridgeTx), destTxHash); + } + + function claim(address caller, IFastBridgeV2.BridgeTransactionV2 memory bridgeTx) public { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.claim(abi.encode(bridgeTx)); + } + + function claim(address caller, IFastBridgeV2.BridgeTransactionV2 memory bridgeTx, address to) public { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.claim(abi.encode(bridgeTx), to); + } + + function dispute(address caller, bytes32 txId) public { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.dispute(txId); + } + + function refund(address caller, IFastBridgeV2.BridgeTransactionV2 memory bridgeTx) public { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.refund(abi.encode(bridgeTx)); + } + + function test_nonce() public view { + uint256 result = fastBridge.nonce(); + // deprecated. should always return zero in FbV2. + assertEq(result, 0); + } + + function assertEq(FastBridgeV2.BridgeStatus a, FastBridgeV2.BridgeStatus b) public pure { + assertEq(uint8(a), uint8(b)); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.Exclusivity.Negative.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.Exclusivity.Negative.t.sol new file mode 100644 index 0000000000..0b19f097f3 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.Exclusivity.Negative.t.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2SrcTest, IFastBridge, IFastBridgeV2} from "./FastBridgeV2.Src.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2SrcExclusivityNegativeTest is FastBridgeV2SrcTest { + uint256 public constant EXCLUSIVITY_PERIOD_ABS = 60 seconds; + + function createFixturesV2() public virtual override { + tokenParamsV2.quoteRelayer = relayerA; + tokenParamsV2.quoteExclusivitySeconds = -int256(EXCLUSIVITY_PERIOD_ABS); + tokenParamsV2.quoteId = bytes("Created by Relayer A"); + ethParamsV2.quoteRelayer = relayerB; + ethParamsV2.quoteExclusivitySeconds = -int256(EXCLUSIVITY_PERIOD_ABS); + ethParamsV2.quoteId = bytes("Created by Relayer B"); + + tokenTx.exclusivityRelayer = relayerA; + tokenTx.exclusivityEndTime = block.timestamp - EXCLUSIVITY_PERIOD_ABS; + ethTx.exclusivityRelayer = relayerB; + ethTx.exclusivityEndTime = block.timestamp - EXCLUSIVITY_PERIOD_ABS; + } + + function bridge(address caller, uint256 msgValue, IFastBridge.BridgeParams memory params) public virtual override { + IFastBridgeV2.BridgeParamsV2 memory paramsV2 = params.originToken == ETH_ADDRESS ? ethParamsV2 : tokenParamsV2; + bridge(caller, msgValue, params, paramsV2); + } + + function test_bridge_revert_exclusivityEndTimeZero() public { + tokenParamsV2.quoteExclusivitySeconds = -int256(block.timestamp); + vm.expectRevert(ExclusivityParamsIncorrect.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams, paramsV2: tokenParamsV2}); + } + + function test_bridge_revert_exclusivityPeriodUnderflow() public { + tokenParamsV2.quoteExclusivitySeconds = -int256(block.timestamp + 1); + vm.expectRevert(ExclusivityParamsIncorrect.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams, paramsV2: tokenParamsV2}); + } + + function test_bridge_eth_revert_exclusivityEndTimeZero() public { + ethParamsV2.quoteExclusivitySeconds = -int256(block.timestamp); + vm.expectRevert(ExclusivityParamsIncorrect.selector); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams, paramsV2: ethParamsV2}); + } + + function test_bridge_eth_revert_exclusivityPeriodUnderflow() public { + ethParamsV2.quoteExclusivitySeconds = -int256(block.timestamp + 1); + vm.expectRevert(ExclusivityParamsIncorrect.selector); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams, paramsV2: ethParamsV2}); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.Exclusivity.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.Exclusivity.t.sol new file mode 100644 index 0000000000..259ef0bacd --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.Exclusivity.t.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2SrcTest, IFastBridge, IFastBridgeV2} from "./FastBridgeV2.Src.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2SrcExclusivityTest is FastBridgeV2SrcTest { + uint256 public constant EXCLUSIVITY_PERIOD = 60 seconds; + + function createFixturesV2() public virtual override { + tokenParamsV2.quoteRelayer = relayerA; + tokenParamsV2.quoteExclusivitySeconds = int256(EXCLUSIVITY_PERIOD); + tokenParamsV2.quoteId = bytes("Created by Relayer A"); + ethParamsV2.quoteRelayer = relayerB; + ethParamsV2.quoteExclusivitySeconds = int256(EXCLUSIVITY_PERIOD); + ethParamsV2.quoteId = bytes("Created by Relayer B"); + + tokenTx.exclusivityRelayer = relayerA; + tokenTx.exclusivityEndTime = block.timestamp + EXCLUSIVITY_PERIOD; + ethTx.exclusivityRelayer = relayerB; + ethTx.exclusivityEndTime = block.timestamp + EXCLUSIVITY_PERIOD; + } + + function bridge(address caller, uint256 msgValue, IFastBridge.BridgeParams memory params) public virtual override { + IFastBridgeV2.BridgeParamsV2 memory paramsV2 = params.originToken == ETH_ADDRESS ? ethParamsV2 : tokenParamsV2; + bridge(caller, msgValue, params, paramsV2); + } + + function test_bridge_revert_exclusivityEndTimeOverDeadline() public { + tokenParamsV2.quoteExclusivitySeconds = int256(DEADLINE + 1); + vm.expectRevert(ExclusivityParamsIncorrect.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams, paramsV2: tokenParamsV2}); + } + + function test_bridge_eth_revert_exclusivityEndTimeOverDeadline() public { + ethParamsV2.quoteExclusivitySeconds = int256(DEADLINE + 1); + vm.expectRevert(ExclusivityParamsIncorrect.selector); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams, paramsV2: ethParamsV2}); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.ProtocolFees.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.ProtocolFees.t.sol new file mode 100644 index 0000000000..d9a5324ed1 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.ProtocolFees.t.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2SrcTest} from "./FastBridgeV2.Src.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2SrcProtocolFeesTest is FastBridgeV2SrcTest { + function configureFastBridge() public virtual override { + super.configureFastBridge(); + fastBridge.grantRole(fastBridge.GOVERNOR_ROLE(), address(this)); + fastBridge.setProtocolFeeRate(1e4); // 1% + } + + function createFixtures() public virtual override { + super.createFixtures(); + tokenTx.originFeeAmount = 0.01e6; + tokenTx.originAmount = 0.99e6; + tokenTx.destAmount = 0.98e6; + tokenParams.destAmount = 0.98e6; + ethTx.originFeeAmount = 0.01 ether; + ethTx.originAmount = 0.99 ether; + ethTx.destAmount = 0.98 ether; + ethParams.destAmount = 0.98 ether; + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol new file mode 100644 index 0000000000..c2a5902133 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol @@ -0,0 +1,895 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2SrcBaseTest, IFastBridge, IFastBridgeV2} from "./FastBridgeV2.Src.Base.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { + event BridgeRequested( + bytes32 indexed transactionId, + address indexed sender, + bytes request, + uint32 destChainId, + address originToken, + address destToken, + uint256 originAmount, + uint256 destAmount, + bool sendChainGas + ); + + event BridgeProofProvided(bytes32 indexed transactionId, address indexed relayer, bytes32 transactionHash); + + event BridgeDepositClaimed( + bytes32 indexed transactionId, address indexed relayer, address indexed to, address token, uint256 amount + ); + + event BridgeProofDisputed(bytes32 indexed transactionId, address indexed relayer); + + event BridgeDepositRefunded(bytes32 indexed transactionId, address indexed to, address token, uint256 amount); + + event BridgeQuoteDetails(bytes32 indexed transactionId, bytes quoteId); + + address public claimTo = makeAddr("Claim To"); + + function expectBridgeRequested(IFastBridgeV2.BridgeTransactionV2 memory bridgeTx, bytes32 txId) public { + vm.expectEmit(address(fastBridge)); + emit BridgeRequested({ + transactionId: txId, + sender: bridgeTx.originSender, + request: abi.encode(bridgeTx), + destChainId: bridgeTx.destChainId, + originToken: bridgeTx.originToken, + destToken: bridgeTx.destToken, + originAmount: bridgeTx.originAmount, + destAmount: bridgeTx.destAmount, + sendChainGas: bridgeTx.sendChainGas + }); + } + + function expectBridgeQuoteDetails(bytes32 txId, bytes memory quoteId) public { + vm.expectEmit(address(fastBridge)); + emit BridgeQuoteDetails({transactionId: txId, quoteId: quoteId}); + } + + function expectBridgeProofProvided(bytes32 txId, address relayer, bytes32 destTxHash) public { + vm.expectEmit(address(fastBridge)); + emit BridgeProofProvided({transactionId: txId, relayer: relayer, transactionHash: destTxHash}); + } + + function expectBridgeDepositClaimed( + IFastBridgeV2.BridgeTransactionV2 memory bridgeTx, + bytes32 txId, + address relayer, + address to + ) + public + { + vm.expectEmit(address(fastBridge)); + emit BridgeDepositClaimed({ + transactionId: txId, + relayer: relayer, + to: to, + token: bridgeTx.originToken, + amount: bridgeTx.originAmount + }); + } + + function expectBridgeProofDisputed(bytes32 txId, address guard) public { + vm.expectEmit(address(fastBridge)); + // Note: BridgeProofDisputed event has a mislabeled address parameter, this is actually the guard + emit BridgeProofDisputed({transactionId: txId, relayer: guard}); + } + + function expectBridgeDepositRefunded(IFastBridge.BridgeParams memory bridgeParams, bytes32 txId) public { + vm.expectEmit(address(fastBridge)); + emit BridgeDepositRefunded({ + transactionId: txId, + to: bridgeParams.sender, + token: bridgeParams.originToken, + amount: bridgeParams.originAmount + }); + } + + // ══════════════════════════════════════════════════ BRIDGE ═══════════════════════════════════════════════════════ + + function checkTokenBalancesAfterBridge(address caller) public view { + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + assertEq(srcToken.balanceOf(caller), LEFTOVER_BALANCE); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); + } + + function test_bridge_token() public { + bytes32 txId = getTxId(tokenTx); + expectBridgeRequested(tokenTx, txId); + expectBridgeQuoteDetails(txId, tokenParamsV2.quoteId); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + assertEq(fastBridge.senderNonces(userA), 1); + assertEq(fastBridge.senderNonces(userB), 0); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); + checkTokenBalancesAfterBridge(userA); + } + + function test_bridge_token_diffSender() public { + bytes32 txId = getTxId(tokenTx); + expectBridgeRequested(tokenTx, txId); + expectBridgeQuoteDetails(txId, tokenParamsV2.quoteId); + bridge({caller: userB, msgValue: 0, params: tokenParams}); + assertEq(fastBridge.senderNonces(userA), 1); + assertEq(fastBridge.senderNonces(userB), 0); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); + assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + tokenParams.originAmount); + checkTokenBalancesAfterBridge(userB); + } + + function checkEthBalancesAfterBridge(address caller) public view { + assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); + assertEq(address(caller).balance, LEFTOVER_BALANCE); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethParams.originAmount); + } + + function test_bridge_eth() public { + // bridge token first to match the nonce + bridge({caller: userA, msgValue: 0, params: tokenParams}); + assertEq(fastBridge.senderNonces(userA), 1); + assertEq(fastBridge.senderNonces(userB), 0); + bytes32 txId = getTxId(ethTx); + expectBridgeRequested(ethTx, txId); + expectBridgeQuoteDetails(txId, ethParamsV2.quoteId); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + assertEq(fastBridge.senderNonces(userA), 2); + assertEq(fastBridge.senderNonces(userB), 0); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); + checkEthBalancesAfterBridge(userA); + } + + function test_bridge_eth_diffSender() public { + // bridge token first to match the nonce + bridge({caller: userA, msgValue: 0, params: tokenParams}); + assertEq(fastBridge.senderNonces(userA), 1); + assertEq(fastBridge.senderNonces(userB), 0); + bytes32 txId = getTxId(ethTx); + expectBridgeRequested(ethTx, txId); + expectBridgeQuoteDetails(txId, ethParamsV2.quoteId); + // bridge for user A as sender, called by userB + bridge({caller: userB, msgValue: ethParams.originAmount, params: ethParams}); + assertEq(fastBridge.senderNonces(userA), 2); + assertEq(fastBridge.senderNonces(userB), 0); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); + assertEq(userA.balance, LEFTOVER_BALANCE + ethParams.originAmount); + checkEthBalancesAfterBridge(userB); + } + + function test_bridge_userSpecificNonce() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + assertEq(fastBridge.senderNonces(userA), 1); + assertEq(fastBridge.senderNonces(userB), 0); + // UserB nonce is 0 + ethTx.nonce = 0; + ethParams.sender = userB; + ethTx.originSender = userB; + bytes32 txId = getTxId(ethTx); + expectBridgeRequested(ethTx, txId); + expectBridgeQuoteDetails(txId, ethParamsV2.quoteId); + bridge({caller: userB, msgValue: ethParams.originAmount, params: ethParams}); + assertEq(fastBridge.senderNonces(userA), 1); + assertEq(fastBridge.senderNonces(userB), 1); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); + checkEthBalancesAfterBridge(userB); + } + + function test_bridge_eth_revert_lowerMsgValue() public { + vm.expectRevert(MsgValueIncorrect.selector); + bridge({caller: userA, msgValue: ethParams.originAmount - 1, params: ethParams}); + } + + function test_bridge_eth_revert_higherMsgValue() public { + vm.expectRevert(MsgValueIncorrect.selector); + bridge({caller: userA, msgValue: ethParams.originAmount + 1, params: ethParams}); + } + + function test_bridge_eth_revert_zeroMsgValue() public { + vm.expectRevert(MsgValueIncorrect.selector); + bridge({caller: userA, msgValue: 0, params: ethParams}); + } + + function test_bridge_revert_sameDestinationChain() public { + tokenParams.dstChainId = SRC_CHAIN_ID; + vm.expectRevert(ChainIncorrect.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + } + + function test_bridge_revert_zeroOriginAmount() public { + tokenParams.originAmount = 0; + vm.expectRevert(AmountIncorrect.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + } + + function test_bridge_revert_zeroDestAmount() public { + tokenParams.destAmount = 0; + vm.expectRevert(AmountIncorrect.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + } + + function test_bridge_revert_zeroOriginToken() public { + tokenParams.originToken = address(0); + vm.expectRevert(ZeroAddress.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + } + + function test_bridge_revert_zeroDestToken() public { + tokenParams.destToken = address(0); + vm.expectRevert(ZeroAddress.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + } + + function test_bridge_revert_zeroSender() public { + tokenParams.sender = address(0); + vm.expectRevert(ZeroAddress.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + } + + function test_bridge_revert_zeroRecipient() public { + tokenParams.to = address(0); + vm.expectRevert(ZeroAddress.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + } + + function test_bridge_revert_deadlineTooClose() public { + tokenParams.deadline = block.timestamp + MIN_DEADLINE - 1; + vm.expectRevert(DeadlineTooShort.selector); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + } + + // ═══════════════════════════════════════════════════ PROVE ═══════════════════════════════════════════════════════ + + function test_prove_token() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"01"}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + function test_prove_eth() public { + // bridge token first to match the nonce + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"01"}); + prove({caller: relayerA, bridgeTx: ethTx, destTxHash: hex"01"}); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethParams.originAmount); + assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); + } + + function test_prove_revert_statusNull() public { + vm.expectRevert(StatusIncorrect.selector); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + } + + function test_prove_revert_statusProved() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + vm.expectRevert(StatusIncorrect.selector); + prove({caller: relayerB, bridgeTx: tokenTx, destTxHash: hex"02"}); + } + + function test_prove_revert_statusClaimed() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + vm.expectRevert(StatusIncorrect.selector); + prove({caller: relayerB, bridgeTx: tokenTx, destTxHash: hex"02"}); + } + + function test_prove_revert_statusRefunded() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(DEADLINE + 1); + refund({caller: refunder, bridgeTx: tokenTx}); + vm.expectRevert(StatusIncorrect.selector); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + } + + function test_prove_revert_callerNotRelayer(address caller) public { + vm.assume(caller != relayerA && caller != relayerB); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + expectUnauthorized(caller, fastBridge.RELAYER_ROLE()); + prove({caller: caller, bridgeTx: tokenTx, destTxHash: hex"01"}); + } + + // ════════════════════════════════════════ PROVE OTHER RELAYER ════════════════════════════════════════════ + + function test_proveOther_token() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"01"}); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"01", relayer: relayerA}); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + function test_proveOther_eth() public { + // bridge token first to match the nonce + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"01"}); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"01", relayer: relayerA}); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethParams.originAmount); + assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); + } + + // relayer self-proving using tx id, which is capable of proving for another & most tests focus on that angle. + function test_proveOther_self() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"01"}); + prove({caller: relayerA, transactionId: txId, destTxHash: hex"01", relayer: relayerA}); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + // arbitrary non-privileged address can be asserted as the relayer + function test_proveOther_permless() public { + bytes32 txId = getTxId(tokenTx); + + bridge({caller: userA, msgValue: 0, params: tokenParams}); + expectBridgeProofProvided({txId: txId, relayer: address(0x1234), destTxHash: hex"01"}); + prove({caller: relayerA, transactionId: txId, destTxHash: hex"01", relayer: address(0x1234)}); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, address(0x1234)); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + function test_proveOther_reProveAfterDispute() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"01"}); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"01", relayer: relayerA}); + expectBridgeProofDisputed(txId, guard); + dispute(guard, txId); + expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"02"}); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"02", relayer: relayerA}); + expectBridgeProofDisputed(txId, guard); + dispute(guard, txId); + expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"03"}); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"03", relayer: relayerA}); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + // can prove long after relaying as long as status is still good + function test_proveOther_longDelay() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(10 days); + expectBridgeProofProvided({txId: txId, relayer: relayerA, destTxHash: hex"01"}); + prove({caller: relayerA, transactionId: txId, destTxHash: hex"01", relayer: relayerA}); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + function test_proveOther_revert_statusProved() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"01", relayer: relayerA}); + vm.expectRevert(StatusIncorrect.selector); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"02", relayer: relayerA}); + } + + function test_proveOther_revert_statusClaimed() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"01", relayer: relayerA}); + skip(CLAIM_DELAY + 1); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + vm.expectRevert(StatusIncorrect.selector); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"02", relayer: relayerA}); + } + + function test_proveOther_revert_statusRefunded() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(DEADLINE + 1); + refund({caller: refunder, bridgeTx: tokenTx}); + vm.expectRevert(StatusIncorrect.selector); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"01", relayer: relayerA}); + } + + function test_proveOther_revert_callerNotAuthed(address caller) public { + bytes32 txId = getTxId(tokenTx); + vm.assume(caller != relayerA && caller != relayerB); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + expectUnauthorized(caller, fastBridge.RELAYER_ROLE()); + prove({caller: caller, transactionId: txId, destTxHash: hex"01", relayer: relayerA}); + } + + // ═══════════════════════════════════════════════════ CLAIM ═══════════════════════════════════════════════════════ + + function checkTokenBalancesAfterClaim(address relayer) public view { + uint256 expectedProtocolFees = INITIAL_PROTOCOL_FEES_TOKEN + tokenTx.originFeeAmount; + assertEq(fastBridge.protocolFees(address(srcToken)), expectedProtocolFees); + assertEq(srcToken.balanceOf(relayer), tokenTx.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), expectedProtocolFees); + } + + function test_claim_token() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + assertTrue(fastBridge.canClaim(txId, relayerA)); + expectBridgeDepositClaimed({bridgeTx: tokenTx, txId: txId, relayer: relayerA, to: relayerA}); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + checkTokenBalancesAfterClaim(relayerA); + } + + function test_claim_token_permissionless(address caller) public { + vm.assume(caller != relayerA); + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + expectBridgeDepositClaimed({bridgeTx: tokenTx, txId: txId, relayer: relayerA, to: relayerA}); + claim({caller: caller, bridgeTx: tokenTx}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + checkTokenBalancesAfterClaim(relayerA); + } + + function test_claim_token_permissionless_toZeroAddress(address caller) public { + vm.assume(caller != relayerA); + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + expectBridgeDepositClaimed({bridgeTx: tokenTx, txId: txId, relayer: relayerA, to: relayerA}); + claim({caller: caller, bridgeTx: tokenTx, to: address(0)}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + checkTokenBalancesAfterClaim(relayerA); + } + + function test_claim_token_toDiffAddress() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + expectBridgeDepositClaimed({bridgeTx: tokenTx, txId: txId, relayer: relayerA, to: claimTo}); + claim({caller: relayerA, bridgeTx: tokenTx, to: claimTo}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(srcToken.balanceOf(relayerA), 0); + checkTokenBalancesAfterClaim(claimTo); + } + + function test_claim_token_longDelay() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 30 days); + expectBridgeDepositClaimed({bridgeTx: tokenTx, txId: txId, relayer: relayerA, to: relayerA}); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + checkTokenBalancesAfterClaim(relayerA); + } + + function checkEthBalancesAfterClaim(address relayer) public view { + uint256 expectedProtocolFees = INITIAL_PROTOCOL_FEES_ETH + ethTx.originFeeAmount; + assertEq(fastBridge.protocolFees(ETH_ADDRESS), expectedProtocolFees); + assertEq(address(relayer).balance, ethTx.originAmount); + assertEq(address(fastBridge).balance, expectedProtocolFees); + } + + function test_claim_eth() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + prove({caller: relayerA, bridgeTx: ethTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + assertTrue(fastBridge.canClaim(txId, relayerA)); + expectBridgeDepositClaimed({bridgeTx: ethTx, txId: txId, relayer: relayerA, to: relayerA}); + claim({caller: relayerA, bridgeTx: ethTx, to: relayerA}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + checkEthBalancesAfterClaim(relayerA); + } + + function test_claim_eth_permissionless(address caller) public { + vm.assume(caller != relayerA); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + prove({caller: relayerA, bridgeTx: ethTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + expectBridgeDepositClaimed({bridgeTx: ethTx, txId: txId, relayer: relayerA, to: relayerA}); + claim({caller: caller, bridgeTx: ethTx}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + checkEthBalancesAfterClaim(relayerA); + } + + function test_claim_eth_permissionless_toZeroAddress(address caller) public { + vm.assume(caller != relayerA); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + prove({caller: relayerA, bridgeTx: ethTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + expectBridgeDepositClaimed({bridgeTx: ethTx, txId: txId, relayer: relayerA, to: relayerA}); + claim({caller: caller, bridgeTx: ethTx, to: address(0)}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + checkEthBalancesAfterClaim(relayerA); + } + + function test_claim_eth_toDiffAddress() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + prove({caller: relayerA, bridgeTx: ethTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + expectBridgeDepositClaimed({bridgeTx: ethTx, txId: txId, relayer: relayerA, to: claimTo}); + claim({caller: relayerA, bridgeTx: ethTx, to: claimTo}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + checkEthBalancesAfterClaim(claimTo); + } + + function test_claim_eth_longDelay() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + prove({caller: relayerA, bridgeTx: ethTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 30 days); + expectBridgeDepositClaimed({bridgeTx: ethTx, txId: txId, relayer: relayerA, to: relayerA}); + claim({caller: relayerA, bridgeTx: ethTx, to: relayerA}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + checkEthBalancesAfterClaim(relayerA); + } + + function test_claim_revert_zeroDelay() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + assertFalse(fastBridge.canClaim(getTxId(tokenTx), relayerA)); + vm.expectRevert(DisputePeriodNotPassed.selector); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + } + + function test_claim_revert_smallerDelay() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY); + assertFalse(fastBridge.canClaim(getTxId(tokenTx), relayerA)); + vm.expectRevert(DisputePeriodNotPassed.selector); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + } + + function test_claim_revert_callerNotProven(address caller, address to) public { + vm.assume(caller != relayerA && to != address(0)); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + vm.expectRevert(SenderIncorrect.selector); + fastBridge.canClaim(getTxId(tokenTx), caller); + vm.expectRevert(SenderIncorrect.selector); + claim({caller: caller, bridgeTx: tokenTx, to: to}); + } + + function test_claim_revert_statusNull() public { + bytes32 txId = getTxId(tokenTx); + vm.expectRevert(StatusIncorrect.selector); + fastBridge.canClaim(txId, relayerA); + vm.expectRevert(StatusIncorrect.selector); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + } + + function test_claim_revert_statusRequested() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + vm.expectRevert(StatusIncorrect.selector); + fastBridge.canClaim(txId, relayerA); + vm.expectRevert(StatusIncorrect.selector); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + } + + function test_claim_revert_statusClaimed() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + vm.expectRevert(StatusIncorrect.selector); + fastBridge.canClaim(txId, relayerA); + vm.expectRevert(StatusIncorrect.selector); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + } + + function test_claim_revert_statusRefunded() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(DEADLINE + 1); + refund({caller: refunder, bridgeTx: tokenTx}); + vm.expectRevert(StatusIncorrect.selector); + fastBridge.canClaim(txId, relayerA); + vm.expectRevert(StatusIncorrect.selector); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + } + + // ══════════════════════════════════════════════════ DISPUTE ══════════════════════════════════════════════════════ + + function test_dispute_token() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + expectBridgeProofDisputed({txId: txId, guard: guard}); + dispute({caller: guard, txId: txId}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); + } + + function test_dispute_token_justBeforeDeadline() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY); + expectBridgeProofDisputed({txId: txId, guard: guard}); + dispute({caller: guard, txId: txId}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); + } + + function test_dispute_eth() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + prove({caller: relayerA, bridgeTx: ethTx, destTxHash: hex"01"}); + expectBridgeProofDisputed({txId: txId, guard: guard}); + dispute({caller: guard, txId: txId}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethParams.originAmount); + } + + function test_dispute_eth_justBeforeDeadline() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + prove({caller: relayerA, bridgeTx: ethTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY); + expectBridgeProofDisputed({txId: txId, guard: guard}); + dispute({caller: guard, txId: txId}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethParams.originAmount); + } + + function test_dispute_revert_afterDeadline() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + vm.expectRevert(DisputePeriodPassed.selector); + dispute({caller: guard, txId: txId}); + } + + function test_dispute_revert_callerNotGuard(address caller) public { + vm.assume(caller != guard); + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + expectUnauthorized(caller, fastBridge.GUARD_ROLE()); + dispute({caller: caller, txId: txId}); + } + + function test_dispute_revert_statusNull() public { + bytes32 txId = getTxId(tokenTx); + vm.expectRevert(StatusIncorrect.selector); + dispute({caller: guard, txId: txId}); + } + + function test_dispute_revert_statusRequested() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + vm.expectRevert(StatusIncorrect.selector); + dispute({caller: guard, txId: txId}); + } + + function test_dispute_revert_statusClaimed() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + vm.expectRevert(StatusIncorrect.selector); + dispute({caller: guard, txId: txId}); + } + + function test_dispute_revert_statusRefunded() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(DEADLINE + 1); + refund({caller: refunder, bridgeTx: tokenTx}); + vm.expectRevert(StatusIncorrect.selector); + dispute({caller: guard, txId: txId}); + } + + // ══════════════════════════════════════════════════ REFUND ═══════════════════════════════════════════════════════ + + function test_refund_token() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(DEADLINE + 1); + expectBridgeDepositRefunded({bridgeParams: tokenParams, txId: txId}); + refund({caller: refunder, bridgeTx: tokenTx}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + tokenParams.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + /// @notice Deposit should be refunded to the BridgeParams.sender, regardless of the actual caller + function test_refund_token_diffSender() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userB, msgValue: 0, params: tokenParams}); + skip(DEADLINE + 1); + expectBridgeDepositRefunded({bridgeParams: tokenParams, txId: txId}); + refund({caller: refunder, bridgeTx: tokenTx}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + 2 * tokenParams.originAmount); + assertEq(srcToken.balanceOf(userB), LEFTOVER_BALANCE); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + function test_refund_token_longDelay() public { + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(DEADLINE + 30 days); + expectBridgeDepositRefunded({bridgeParams: tokenParams, txId: txId}); + refund({caller: refunder, bridgeTx: tokenTx}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + tokenParams.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + function test_refund_token_permisionless(address caller) public { + vm.assume(caller != refunder); + bytes32 txId = getTxId(tokenTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(DEADLINE + PERMISSIONLESS_REFUND_DELAY + 1); + expectBridgeDepositRefunded({bridgeParams: tokenParams, txId: txId}); + refund({caller: caller, bridgeTx: tokenTx}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.protocolFees(address(srcToken)), INITIAL_PROTOCOL_FEES_TOKEN); + assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + tokenParams.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + function test_refund_eth() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + skip(DEADLINE + 1); + expectBridgeDepositRefunded({bridgeParams: ethParams, txId: txId}); + refund({caller: refunder, bridgeTx: ethTx}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); + assertEq(userA.balance, LEFTOVER_BALANCE + ethParams.originAmount); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH); + } + + /// @notice Deposit should be refunded to the BridgeParams.sender, regardless of the actual caller + function test_refund_eth_diffSender() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userB, msgValue: ethParams.originAmount, params: ethParams}); + skip(DEADLINE + 1); + expectBridgeDepositRefunded({bridgeParams: ethParams, txId: txId}); + refund({caller: refunder, bridgeTx: ethTx}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); + assertEq(userA.balance, LEFTOVER_BALANCE + 2 * ethParams.originAmount); + assertEq(userB.balance, LEFTOVER_BALANCE); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH); + } + + function test_refund_eth_longDelay() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + skip(DEADLINE + 30 days); + expectBridgeDepositRefunded({bridgeParams: ethParams, txId: txId}); + refund({caller: refunder, bridgeTx: ethTx}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); + assertEq(userA.balance, LEFTOVER_BALANCE + ethParams.originAmount); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH); + } + + function test_refund_eth_permisionless(address caller) public { + vm.assume(caller != refunder); + bytes32 txId = getTxId(ethTx); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + skip(DEADLINE + PERMISSIONLESS_REFUND_DELAY + 1); + expectBridgeDepositRefunded({bridgeParams: ethParams, txId: txId}); + refund({caller: caller, bridgeTx: ethTx}); + assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED); + assertEq(fastBridge.protocolFees(ETH_ADDRESS), INITIAL_PROTOCOL_FEES_ETH); + assertEq(userA.balance, LEFTOVER_BALANCE + ethParams.originAmount); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH); + } + + function test_refund_revert_zeroDelay() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + vm.expectRevert(DeadlineNotExceeded.selector); + refund({caller: refunder, bridgeTx: tokenTx}); + } + + function test_refund_revert_justBeforeDeadline() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(DEADLINE); + vm.expectRevert(DeadlineNotExceeded.selector); + refund({caller: refunder, bridgeTx: tokenTx}); + } + + function test_refund_revert_justBeforeDeadline_permisionless(address caller) public { + vm.assume(caller != refunder); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(DEADLINE + PERMISSIONLESS_REFUND_DELAY); + vm.expectRevert(DeadlineNotExceeded.selector); + refund({caller: caller, bridgeTx: tokenTx}); + } + + function test_refund_revert_statusNull() public { + vm.expectRevert(StatusIncorrect.selector); + refund({caller: refunder, bridgeTx: ethTx}); + } + + function test_refund_revert_statusProven() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + vm.expectRevert(StatusIncorrect.selector); + refund({caller: refunder, bridgeTx: tokenTx}); + } + + function test_refund_revert_statusClaimed() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + skip(CLAIM_DELAY + 1); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerA}); + vm.expectRevert(StatusIncorrect.selector); + refund({caller: refunder, bridgeTx: tokenTx}); + } + + function test_refund_revert_statusRefunded() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + skip(DEADLINE + 1); + refund({caller: refunder, bridgeTx: tokenTx}); + vm.expectRevert(StatusIncorrect.selector); + refund({caller: refunder, bridgeTx: tokenTx}); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.t.sol b/packages/contracts-rfq/test/FastBridgeV2.t.sol new file mode 100644 index 0000000000..3828076dac --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.t.sol @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {IFastBridge} from "../contracts/interfaces/IFastBridge.sol"; + +// solhint-disable-next-line no-unused-import +import {IFastBridgeV2} from "../contracts/interfaces/IFastBridgeV2.sol"; + +import {IFastBridgeV2Errors} from "../contracts/interfaces/IFastBridgeV2Errors.sol"; +import {FastBridgeV2} from "../contracts/FastBridgeV2.sol"; + +import {MockERC20} from "./MockERC20.sol"; + +import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; +import {Test} from "forge-std/Test.sol"; +import {stdStorage, StdStorage} from "forge-std/Test.sol"; + +// solhint-disable no-empty-blocks, max-states-count, ordering +abstract contract FastBridgeV2Test is Test, IFastBridgeV2Errors { + using stdStorage for StdStorage; + + uint32 public constant SRC_CHAIN_ID = 1337; + uint32 public constant DST_CHAIN_ID = 7331; + uint256 public constant DEADLINE = 1 days; + address public constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + + FastBridgeV2 public fastBridge; + MockERC20 public srcToken; + MockERC20 public dstToken; + + address public relayerA = makeAddr("Relayer A"); + address public relayerB = makeAddr("Relayer B"); + address public guard = makeAddr("Guard"); + address public userA = makeAddr("User A"); + address public userB = makeAddr("User B"); + address public governor = makeAddr("Governor"); + address public refunder = makeAddr("Refunder"); + + IFastBridgeV2.BridgeTransactionV2 internal tokenTx; + IFastBridgeV2.BridgeTransactionV2 internal ethTx; + IFastBridge.BridgeParams internal tokenParams; + IFastBridge.BridgeParams internal ethParams; + + IFastBridgeV2.BridgeParamsV2 internal tokenParamsV2; + IFastBridgeV2.BridgeParamsV2 internal ethParamsV2; + + function setUp() public virtual { + srcToken = new MockERC20("SrcToken", 6); + dstToken = new MockERC20("DstToken", 6); + createFixtures(); + createFixturesV2(); + fastBridge = deployFastBridge(); + configureFastBridge(); + mintTokens(); + } + + function deployFastBridge() public virtual returns (FastBridgeV2); + + function configureFastBridge() public virtual {} + + function mintTokens() public virtual {} + + function createFixtures() public virtual { + tokenParams = IFastBridge.BridgeParams({ + dstChainId: DST_CHAIN_ID, + sender: userA, + to: userB, + originToken: address(srcToken), + destToken: address(dstToken), + originAmount: 1e6, + destAmount: 0.99e6, + sendChainGas: false, + deadline: block.timestamp + DEADLINE + }); + ethParams = IFastBridge.BridgeParams({ + dstChainId: DST_CHAIN_ID, + sender: userA, + to: userB, + originToken: ETH_ADDRESS, + destToken: ETH_ADDRESS, + originAmount: 1 ether, + destAmount: 0.99 ether, + sendChainGas: false, + deadline: block.timestamp + DEADLINE + }); + + setStorageBridgeTxV2( + tokenTx, + IFastBridge.BridgeTransaction({ + originChainId: SRC_CHAIN_ID, + destChainId: DST_CHAIN_ID, + originSender: userA, + destRecipient: userB, + originToken: address(srcToken), + destToken: address(dstToken), + originAmount: 1e6, + destAmount: 0.99e6, + // override this in tests with protocol fees + originFeeAmount: 0, + sendChainGas: false, + deadline: block.timestamp + DEADLINE, + nonce: 0 + }) + ); + setStorageBridgeTxV2( + ethTx, + IFastBridge.BridgeTransaction({ + originChainId: SRC_CHAIN_ID, + destChainId: DST_CHAIN_ID, + originSender: userA, + destRecipient: userB, + originToken: ETH_ADDRESS, + destToken: ETH_ADDRESS, + originAmount: 1 ether, + destAmount: 0.99 ether, + // override this in tests with protocol fees + originFeeAmount: 0, + sendChainGas: false, + deadline: block.timestamp + DEADLINE, + nonce: 1 + }) + ); + } + + function createFixturesV2() public virtual { + // Override in tests with exclusivity params + tokenParamsV2 = + IFastBridgeV2.BridgeParamsV2({quoteRelayer: address(0), quoteExclusivitySeconds: 0, quoteId: ""}); + ethParamsV2 = IFastBridgeV2.BridgeParamsV2({quoteRelayer: address(0), quoteExclusivitySeconds: 0, quoteId: ""}); + + tokenTx.exclusivityRelayer = address(0); + tokenTx.exclusivityEndTime = block.timestamp; + ethTx.exclusivityRelayer = address(0); + ethTx.exclusivityEndTime = block.timestamp; + } + + function setStorageBridgeTxV2( + IFastBridgeV2.BridgeTransactionV2 storage txV2, + IFastBridge.BridgeTransaction memory txV1 + ) + internal + { + txV2.originChainId = txV1.originChainId; + txV2.destChainId = txV1.destChainId; + txV2.originSender = txV1.originSender; + txV2.destRecipient = txV1.destRecipient; + txV2.originToken = txV1.originToken; + txV2.destToken = txV1.destToken; + txV2.originAmount = txV1.originAmount; + txV2.destAmount = txV1.destAmount; + txV2.originFeeAmount = txV1.originFeeAmount; + txV2.sendChainGas = txV1.sendChainGas; + txV2.deadline = txV1.deadline; + txV2.nonce = txV1.nonce; + } + + function extractV1(IFastBridgeV2.BridgeTransactionV2 memory txV2) + public + pure + returns (IFastBridge.BridgeTransaction memory txV1) + { + txV1.originChainId = txV2.originChainId; + txV1.destChainId = txV2.destChainId; + txV1.originSender = txV2.originSender; + txV1.destRecipient = txV2.destRecipient; + txV1.originToken = txV2.originToken; + txV1.destToken = txV2.destToken; + txV1.originAmount = txV2.originAmount; + txV1.destAmount = txV2.destAmount; + txV1.originFeeAmount = txV2.originFeeAmount; + txV1.sendChainGas = txV2.sendChainGas; + txV1.deadline = txV2.deadline; + txV1.nonce = txV2.nonce; + } + + function getTxId(IFastBridgeV2.BridgeTransactionV2 memory bridgeTx) public pure returns (bytes32) { + return keccak256(abi.encode(bridgeTx)); + } + + function expectUnauthorized(address caller, bytes32 role) public { + vm.expectRevert(abi.encodeWithSelector(IAccessControl.AccessControlUnauthorizedAccount.selector, caller, role)); + } + + function cheatCollectedProtocolFees(address token, uint256 amount) public { + stdstore.target(address(fastBridge)).sig("protocolFees(address)").with_key(token).checked_write(amount); + } +} diff --git a/packages/contracts-rfq/test/MockERC20.sol b/packages/contracts-rfq/test/MockERC20.sol index 9736a896c7..f4c07d49aa 100644 --- a/packages/contracts-rfq/test/MockERC20.sol +++ b/packages/contracts-rfq/test/MockERC20.sol @@ -10,10 +10,6 @@ contract MockERC20 is ERC20 { _decimals = decimals_; } - function decimals() public view override returns (uint8) { - return _decimals; - } - function burn(address account, uint256 amount) external { _burn(account, amount); } @@ -21,4 +17,8 @@ contract MockERC20 is ERC20 { function mint(address account, uint256 amount) external { _mint(account, amount); } + + function decimals() public view override returns (uint8) { + return _decimals; + } } diff --git a/packages/contracts-rfq/test/UniversalTokenLib.t.sol b/packages/contracts-rfq/test/UniversalTokenLib.t.sol index 19e796e52d..58bbd34435 100644 --- a/packages/contracts-rfq/test/UniversalTokenLib.t.sol +++ b/packages/contracts-rfq/test/UniversalTokenLib.t.sol @@ -8,6 +8,7 @@ import {MockRevertingRecipient} from "./MockRevertingRecipient.sol"; import {Test} from "forge-std/Test.sol"; +// solhint-disable ordering contract UniversalTokenLibraryTest is Test { UniversalTokenLibHarness public libHarness; MockERC20 public token; diff --git a/packages/contracts-rfq/test/UniversalTokenLibHarness.sol b/packages/contracts-rfq/test/UniversalTokenLibHarness.sol index b4ea21182b..7f8d2d6753 100644 --- a/packages/contracts-rfq/test/UniversalTokenLibHarness.sol +++ b/packages/contracts-rfq/test/UniversalTokenLibHarness.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.17; import {UniversalTokenLib} from "../contracts/libs/UniversalToken.sol"; +// solhint-disable ordering contract UniversalTokenLibHarness { function universalTransfer(address token, address to, uint256 value) public { UniversalTokenLib.universalTransfer(token, to, value); diff --git a/packages/explorer-ui/CHANGELOG.md b/packages/explorer-ui/CHANGELOG.md index 73cdb24b88..87d7ffe6be 100644 --- a/packages/explorer-ui/CHANGELOG.md +++ b/packages/explorer-ui/CHANGELOG.md @@ -3,6 +3,54 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.3.8](https://github.com/synapsecns/sanguine/compare/@synapsecns/explorer-ui@0.3.7...@synapsecns/explorer-ui@0.3.8) (2024-10-01) + +**Note:** Version bump only for package @synapsecns/explorer-ui + + + + + +## [0.3.7](https://github.com/synapsecns/sanguine/compare/@synapsecns/explorer-ui@0.3.6...@synapsecns/explorer-ui@0.3.7) (2024-10-01) + +**Note:** Version bump only for package @synapsecns/explorer-ui + + + + + +## [0.3.6](https://github.com/synapsecns/sanguine/compare/@synapsecns/explorer-ui@0.3.5...@synapsecns/explorer-ui@0.3.6) (2024-10-01) + +**Note:** Version bump only for package @synapsecns/explorer-ui + + + + + +## [0.3.5](https://github.com/synapsecns/sanguine/compare/@synapsecns/explorer-ui@0.3.4...@synapsecns/explorer-ui@0.3.5) (2024-10-01) + +**Note:** Version bump only for package @synapsecns/explorer-ui + + + + + +## [0.3.4](https://github.com/synapsecns/sanguine/compare/@synapsecns/explorer-ui@0.3.3...@synapsecns/explorer-ui@0.3.4) (2024-09-30) + +**Note:** Version bump only for package @synapsecns/explorer-ui + + + + + +## [0.3.3](https://github.com/synapsecns/sanguine/compare/@synapsecns/explorer-ui@0.3.2...@synapsecns/explorer-ui@0.3.3) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/explorer-ui + + + + + ## [0.3.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/explorer-ui@0.3.1...@synapsecns/explorer-ui@0.3.2) (2024-09-10) **Note:** Version bump only for package @synapsecns/explorer-ui diff --git a/packages/explorer-ui/components/misc/AssetImage.tsx b/packages/explorer-ui/components/misc/AssetImage.tsx index 570d70429e..5206152f11 100644 --- a/packages/explorer-ui/components/misc/AssetImage.tsx +++ b/packages/explorer-ui/components/misc/AssetImage.tsx @@ -11,7 +11,13 @@ export const AssetImage = ({ tokenAddress, chainId, className }) => {
- +
@@ -29,13 +35,21 @@ export const AssetImage = ({ tokenAddress, chainId, className }) => { className={`inline mr-[.5rem] rounded-full ${className}`} src={t?.icon} alt="" + height={16} + width={16} /> ) } else { return ( // temporary fix until either symbolToToken works better as a function or explorer indexer has the right token addresses - + // { src={chain.chainImg} className={`${imgSize} rounded-full mr-2 inline ${className}`} alt={chain.name} + height={16} + width={16} /> ) } else { diff --git a/packages/explorer-ui/components/misc/ChainInfo.tsx b/packages/explorer-ui/components/misc/ChainInfo.tsx index d494281bba..06a40e0980 100644 --- a/packages/explorer-ui/components/misc/ChainInfo.tsx +++ b/packages/explorer-ui/components/misc/ChainInfo.tsx @@ -45,6 +45,8 @@ export const ChainInfo = ({ className={`inline rounded-full ${imgClassName}`} src={chain?.chainImg} alt={chain?.name} + height={16} + width={16} />

=18.17.0" @@ -38,7 +38,7 @@ "recharts": "^2.3.2", "sharp": "^0.31.3", "swr": "^1.3.0", - "synapse-constants": "^1.3.24", + "synapse-constants": "^1.5.6", "tailwind-merge": "^1.3.0", "tiny-warning": "^1.0.3", "web-vitals": "^2.1.4" diff --git a/packages/explorer-ui/pages/tx/[kappa].tsx b/packages/explorer-ui/pages/tx/[kappa].tsx index 05ac17ca7e..75bfb80b9a 100644 --- a/packages/explorer-ui/pages/tx/[kappa].tsx +++ b/packages/explorer-ui/pages/tx/[kappa].tsx @@ -10,7 +10,7 @@ import { API_URL } from '@graphql' import { HorizontalDivider } from '@components/misc/HorizontalDivider' import { formatDateTimestamp } from '@utils/formatDate' import { IconAndAmount } from '@components/misc/IconAndAmount' - +import { addressToSymbol } from '@utils/addressToSymbol' const CHAINS_BY_ID = CHAINS.CHAINS_BY_ID const link = new HttpLink({ @@ -157,7 +157,10 @@ export const BridgeTransaction = ({ queryResult }) => { value={fromInfo.value} tokenAddress={fromInfo.tokenAddress} chainId={fromInfo.chainID} - tokenSymbol={fromInfo.tokenSymbol} + tokenSymbol={addressToSymbol({ + tokenAddress: fromInfo.tokenAddress, + chainId: fromInfo.chainID, + })} iconSize="w-4 h-4" // textSize="text-sm" // styledCoin={true} @@ -183,7 +186,10 @@ export const BridgeTransaction = ({ queryResult }) => { value={toInfo.value} tokenAddress={toInfo.tokenAddress} chainId={toInfo.chainID} - tokenSymbol={toInfo.tokenSymbol} + tokenSymbol={addressToSymbol({ + tokenAddress: toInfo.tokenAddress, + chainId: toInfo.chainID, + })} iconSize="w-4 h-4" // textSize="text-sm" // styledCoin={true} diff --git a/packages/rest-api/CHANGELOG.md b/packages/rest-api/CHANGELOG.md index f65c063354..1c017e63fa 100644 --- a/packages/rest-api/CHANGELOG.md +++ b/packages/rest-api/CHANGELOG.md @@ -3,6 +3,79 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.3.4](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.3.3...@synapsecns/rest-api@1.3.4) (2024-09-29) + +**Note:** Version bump only for package @synapsecns/rest-api + + + + + +## [1.3.3](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.3.2...@synapsecns/rest-api@1.3.3) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/rest-api + + + + + +## [1.3.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.3.1...@synapsecns/rest-api@1.3.2) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/rest-api + + + + + +## [1.3.1](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.3.0...@synapsecns/rest-api@1.3.1) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/rest-api + + + + + +# [1.3.0](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.2.0...@synapsecns/rest-api@1.3.0) (2024-09-25) + + +### Features + +* **rest-api:** Adds validateRouteExists validation [SLT-260] ([#3180](https://github.com/synapsecns/sanguine/issues/3180)) ([ceff8bc](https://github.com/synapsecns/sanguine/commit/ceff8bcbc179dd6442cd1861eb31505217f9c55c)) + + + + + +# [1.2.0](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.1.5...@synapsecns/rest-api@1.2.0) (2024-09-24) + + +### Features + +* **api:** bridge limits [SLT-165] ([#3179](https://github.com/synapsecns/sanguine/issues/3179)) ([98362bb](https://github.com/synapsecns/sanguine/commit/98362bb7cd8972d83d7a628b2db4fb06831871c0)) + + + + + +## [1.1.5](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.1.4...@synapsecns/rest-api@1.1.5) (2024-09-23) + + +### Bug Fixes + +* formatted bridge fee amount ([#3165](https://github.com/synapsecns/sanguine/issues/3165)) ([b6651b1](https://github.com/synapsecns/sanguine/commit/b6651b1a19fbb6341ddf4bb3303d7aa9fa6f616e)) + + + + + +## [1.1.4](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.1.3...@synapsecns/rest-api@1.1.4) (2024-09-21) + +**Note:** Version bump only for package @synapsecns/rest-api + + + + + ## [1.1.3](https://github.com/synapsecns/sanguine/compare/@synapsecns/rest-api@1.1.2...@synapsecns/rest-api@1.1.3) (2024-09-20) **Note:** Version bump only for package @synapsecns/rest-api diff --git a/packages/rest-api/README.md b/packages/rest-api/README.md index baab635b4b..3b83d587dd 100644 --- a/packages/rest-api/README.md +++ b/packages/rest-api/README.md @@ -1,81 +1,77 @@ -# Swap/Bridge REST API Quoter +# Synapse REST API +To make requests, use https://api.synapseprotocol.com/ -To run locally: -\`npm start\` - -To make requests, use https://synapse-rest-api-v2.herokuapp.com/ -The Synapse Rest API supports four main functions +To run locally: +```bash +yarn dev +``` -## /swap +# Documentation +[Swagger Documentation](https://api.synapseprotocol.com/api-docs/) -which returns the following +[GitBook Documentation](https://docs.synapseprotocol.com/developers/rest-api) -- \`routerAddress\` (string) - The address of the router contract -- \`maxAmountOut\` (object) - The maximum amount of tokens that can be swapped out. Contains: - - \`type\` (string) - The data type - - \`hex\` (string) - The amount encoded in hexidecimal -- \`query\` (object) - Parameters for the swap query: - - \`0\` (string) - Router contract address - - \`1\` (string) - Address of tokenIn - - \`2\` (object) - Amount of tokenIn to swap (same structure as maxAmountOut) - - \`3\` (object) - Minimum amount of tokenOut requested (same structure as maxAmountOut) - - \`4\` (string) - Encoded params for swap routing - - \`swapAdapter\` (string) - Address of the swap adapter contract - - \`tokenOut\` (string) - Address of tokenOut - - \`minAmountOut\` (object) - Minimum amount of tokenOut required (same structure as maxAmountOut) - - \`deadline\` (object) - Deadline parameter for the swap (same structure as maxAmountOut) - - \`rawParams\` (string) - Encoded hex string containing swap parameters -- \`maxAmountOutStr\` (string) - The maxAmountOut value formatted as a decimal string +## REST API +The Synapse REST API provides a set of endpoints for quoting and executing cross-chain token swaps and bridges. It supports various functions including swap quotes, bridge quotes, transaction information retrieval, and token list management. -All \`/swap\` requests should be formatted like such: +## Formatting Requests +Here's an example of how to format a bridge request: -\`/swap?chain=1&fromToken=USDC&toToken=DAI&amount=100\` +```bash +/bridge?fromChain=1&toChain=42161&fromToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&toToken=0xaf88d065e77c8cC2239327C5EDb3A432268e5831&amount=100 +``` -## /bridge +To use this in a basic application: -which returns all transaction information +```javascript +async function getBridgeQuote() { + const response = await fetch('https://api.synapseprotocol.com/bridge?fromChain=1&toChain=42161&fromToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&toToken=0xaf88d065e77c8cC2239327C5EDb3A432268e5831&amount=100'); + const data = await response.json(); + console.log(data); +} +getBridgeQuote(); +``` -- \`feeAmount\` (object) - The fee amount for the swap. Contains: - - \`type\` (string) - Data type - - \`hex\` (string) - Fee amount encoded in hex -- \`feeConfig\` (array) - Fee configuration parameters, contains: - - \`0\` (number) - Gas price - - \`1\` (object) - Fee percentage denominator (hex encoded BigNumber) - - \`2\` (object) - Protocol fee percentage numerator (hex encoded BigNumber) -- \`routerAddress\` (string) - Address of the router contract -- \`maxAmountOut\` (object) - Maximum amount receivable from swap, structure same as above -- \`originQuery\` (object) - Original swap query parameters, contains: - - \`swapAdapter\` (string) - Swap adapter address - - \`tokenOut\` (string) - Address of output token - - \`minAmountOut\` (object) - Minimum output token amount - - \`deadline\` (object) - Expiry time - - \`rawParams\` (string) - Encoded hex params -- \`destQuery\` (object) - Destination swap query parameters, structure similar to originQuery above. -- \`maxAmountOutStr\` (string) - maxAmountOut as a decimal string. -All \`/bridge\` requests should be formatted like such: +## Bridging -\`/bridge?fromChain=1&toChain=42161&fromToken=USDC&toToken=USDC&amount=1000000\` +1. Get a Bridge Quote to confirm the expected amount out and the bridge route -## /swapTxInfo +```javascript +const quoteResponse = await fetch('https://api.synapseprotocol.com/bridge?fromChain=1&toChain=42161&fromToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&toToken=0xaf88d065e77c8cC2239327C5EDb3A432268e5831&amount=100'); +const quoteData = await quoteResponse.json(); +``` -which returns the following +2. Get the structured transaction information -- \`'data'\`: The binary data that forms the input to the transaction. -- \`'to'\`: The address of the Synapse Router (the synapse bridge contract) +```javascript +const txInfoResponse = await fetch('https://api.synapseprotocol.com/bridgeTxInfo?fromChain=1&toChain=42161&fromToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&toToken=0xaf88d065e77c8cC2239327C5EDb3A432268e5831&amount=100&destAddress=0xcc78d2f004c9de9694ff6a9bbdee4793d30f3842'); +const txInfoData = await txInfoResponse.json(); +``` -All \`/swapTxInfo\` requests should be formatted like such: +3. Execute the transaction -\`/swap?chain=1&fromToken=USDC&toToken=DAI&amount=100\` +```javascript +const provider = new ethers.providers.Web3Provider(window.ethereum); +const signer = provider.getSigner(); +const transaction = await signer.sendTransaction(txInfoData); +const receipt = await transaction.wait(); +``` -## /bridgeTxInfo +## Other Functions: +1. `/destinationTokens`: This endpoint provides information about possible destination tokens for a given source chain and token. It's useful for showing users their options when initiating a cross-chain transfer. -which returns the following +```javascript +const response = await fetch('https://api.synapseprotocol.com/destinationTokens?fromChain=1&fromToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'); +const destinationTokens = await response.json(); +``` -- \`'data'\`: The binary data that forms the input to the transaction. -- \`'to'\`: The address of the Synapse Router (the synapse bridge contract) +2. `/bridgeLimits`: This endpoint returns the minimum and maximum amounts that can be bridged for a specific token pair. It's helpful for validating user input and displaying available limits. -All \`/bridgeTxInfo\` requests should be formatted like such: +```javascript +const limitsResponse = await fetch('https://api.synapseprotocol.com/bridgeLimits?fromChain=1&toChain=42161&fromToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&toToken=0xaf88d065e77c8cC2239327C5EDb3A432268e5831'); +const limitsData = await limitsResponse.json(); +``` -\`/bridgeTxInfo?fromChain=1&toChain=42161&fromToken=USDC&toToken=USDC&amount=1000000&destAddress=0xcc78d2f004c9de9694ff6a9bbdee4793d30f3842\` +There are other additional functions that are included and documented in the Swagger documentation. Suggested changes to the API can be made by creating a new branch, making the changes, and opening a pull request. Any changes should include the appropriate test coverage and update the relevant documentation (this README, Swagger, and GitBook). diff --git a/packages/rest-api/package.json b/packages/rest-api/package.json index 10db70e669..fa41f97411 100644 --- a/packages/rest-api/package.json +++ b/packages/rest-api/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/rest-api", - "version": "1.1.3", + "version": "1.3.4", "private": "true", "engines": { "node": ">=18.17.0" @@ -18,10 +18,11 @@ "test:coverage": "jest --collect-coverage" }, "dependencies": { + "@ethersproject/address": "^5.7.0", "@ethersproject/bignumber": "^5.7.0", "@ethersproject/providers": "^5.7.2", "@ethersproject/units": "5.7.0", - "@synapsecns/sdk-router": "^0.11.1", + "@synapsecns/sdk-router": "^0.11.2", "bignumber": "^1.1.0", "ethers": "5.7.2", "express": "^4.18.2", diff --git a/packages/rest-api/src/constants/bridgeable.ts b/packages/rest-api/src/constants/bridgeable.ts index e0bbd61d80..1a9b25a99c 100644 --- a/packages/rest-api/src/constants/bridgeable.ts +++ b/packages/rest-api/src/constants/bridgeable.ts @@ -1,6 +1,7 @@ import { BridgeableToken } from '../types' import { CHAINS } from './chains' -import { ZeroAddress } from '.' + +const NativeTokenAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' export const GOHM: BridgeableToken = { addresses: { @@ -796,16 +797,16 @@ export const NETH: BridgeableToken = { export const ETH: BridgeableToken = { addresses: { - [CHAINS.ETHEREUM.id]: ZeroAddress, - [CHAINS.OPTIMISM.id]: ZeroAddress, - [CHAINS.BOBA.id]: ZeroAddress, + [CHAINS.ETHEREUM.id]: NativeTokenAddress, + [CHAINS.OPTIMISM.id]: NativeTokenAddress, + [CHAINS.BOBA.id]: NativeTokenAddress, [CHAINS.CANTO.id]: '0x5FD55A1B9FC24967C4dB09C513C3BA0DFa7FF687', - [CHAINS.BASE.id]: ZeroAddress, - [CHAINS.ARBITRUM.id]: ZeroAddress, + [CHAINS.BASE.id]: NativeTokenAddress, + [CHAINS.ARBITRUM.id]: NativeTokenAddress, [CHAINS.DFK.id]: '0xfBDF0E31808d0aa7b9509AA6aBC9754E48C58852', - [CHAINS.BLAST.id]: ZeroAddress, - [CHAINS.SCROLL.id]: ZeroAddress, - [CHAINS.LINEA.id]: ZeroAddress, + [CHAINS.BLAST.id]: NativeTokenAddress, + [CHAINS.SCROLL.id]: NativeTokenAddress, + [CHAINS.LINEA.id]: NativeTokenAddress, }, decimals: { [CHAINS.ETHEREUM.id]: 18, @@ -832,7 +833,7 @@ export const ETH: BridgeableToken = { export const MOVR: BridgeableToken = { addresses: { [CHAINS.MOONBEAM.id]: '0x1d4C2a246311bB9f827F4C768e277FF5787B7D7E', - [CHAINS.MOONRIVER.id]: ZeroAddress, + [CHAINS.MOONRIVER.id]: NativeTokenAddress, }, decimals: { [CHAINS.MOONBEAM.id]: 18, @@ -852,7 +853,7 @@ export const AVAX: BridgeableToken = { addresses: { [CHAINS.MOONBEAM.id]: '0xA1f8890E39b4d8E33efe296D698fe42Fb5e59cC3', [CHAINS.KLAYTN.id]: '0xCd8fE44A29Db9159dB36f96570d7A4d91986f528', - [CHAINS.AVALANCHE.id]: ZeroAddress, + [CHAINS.AVALANCHE.id]: NativeTokenAddress, [CHAINS.DFK.id]: '0xB57B60DeBDB0b8172bb6316a9164bd3C695F133a', [CHAINS.HARMONY.id]: '0xb12c13e66AdE1F72f71834f2FC5082Db8C091358', }, @@ -909,7 +910,7 @@ export const WAVAX: BridgeableToken = { export const JEWEL: BridgeableToken = { addresses: { - [CHAINS.DFK.id]: ZeroAddress, + [CHAINS.DFK.id]: NativeTokenAddress, [CHAINS.HARMONY.id]: '0x72cb10c6bfa5624dd07ef608027e366bd690048f', [CHAINS.KLAYTN.id]: '0x30C103f8f5A3A732DFe2dCE1Cc9446f545527b43', [CHAINS.AVALANCHE.id]: '0x997Ddaa07d716995DE90577C123Db411584E5E46', @@ -1158,7 +1159,7 @@ export const DAIe: BridgeableToken = { export const KLAY: BridgeableToken = { addresses: { - [CHAINS.KLAYTN.id]: ZeroAddress, + [CHAINS.KLAYTN.id]: NativeTokenAddress, [CHAINS.DFK.id]: '0x97855Ba65aa7ed2F65Ed832a776537268158B78a', }, decimals: { @@ -1194,7 +1195,7 @@ export const WKLAY: BridgeableToken = { export const MATIC: BridgeableToken = { addresses: { - [CHAINS.POLYGON.id]: ZeroAddress, + [CHAINS.POLYGON.id]: NativeTokenAddress, [CHAINS.DFK.id]: '0xD17a41Cd199edF1093A9Be4404EaDe52Ec19698e', }, decimals: { @@ -1230,7 +1231,7 @@ export const WMATIC: BridgeableToken = { export const FTM: BridgeableToken = { addresses: { - [CHAINS.FANTOM.id]: ZeroAddress, + [CHAINS.FANTOM.id]: NativeTokenAddress, [CHAINS.DFK.id]: '0x2Df041186C844F8a2e2b63F16145Bc6Ff7d23E25', }, decimals: { diff --git a/packages/rest-api/src/controllers/bridgeController.ts b/packages/rest-api/src/controllers/bridgeController.ts index 7bb703631b..f69c8715f0 100644 --- a/packages/rest-api/src/controllers/bridgeController.ts +++ b/packages/rest-api/src/controllers/bridgeController.ts @@ -11,7 +11,14 @@ export const bridgeController = async (req, res) => { return res.status(400).json({ errors: errors.array() }) } try { - const { fromChain, toChain, amount, fromToken, toToken } = req.query + const { + fromChain, + toChain, + amount, + fromToken, + toToken, + originUserAddress, + } = req.query const fromTokenInfo = tokenAddressToToken(fromChain.toString(), fromToken) const toTokenInfo = tokenAddressToToken(toChain.toString(), toToken) @@ -23,19 +30,29 @@ export const bridgeController = async (req, res) => { Number(toChain), fromToken, toToken, - amountInWei + amountInWei, + originUserAddress + ? { originUserAddress: originUserAddress.toString() } + : {} ) - const payload = resp.map((quote) => ({ - ...quote, - maxAmountOutStr: formatBNToString( - quote.maxAmountOut, - toTokenInfo.decimals - ), - bridgeFeeFormatted: formatBNToString( - quote.feeAmount, - toTokenInfo.decimals - ), - })) + + const payload = resp.map((quote) => { + const originQueryTokenOutInfo = tokenAddressToToken( + fromChain.toString(), + quote.originQuery.tokenOut + ) + return { + ...quote, + maxAmountOutStr: formatBNToString( + quote.maxAmountOut, + toTokenInfo.decimals + ), + bridgeFeeFormatted: formatBNToString( + quote.feeAmount, + originQueryTokenOutInfo.decimals + ), + } + }) res.json(payload) } catch (err) { res.status(500).json({ diff --git a/packages/rest-api/src/controllers/bridgeLimitsController.ts b/packages/rest-api/src/controllers/bridgeLimitsController.ts new file mode 100644 index 0000000000..1ef292338f --- /dev/null +++ b/packages/rest-api/src/controllers/bridgeLimitsController.ts @@ -0,0 +1,108 @@ +import { validationResult } from 'express-validator' +import { BigNumber } from 'ethers' +import { parseUnits } from '@ethersproject/units' + +import { Synapse } from '../services/synapseService' +import { tokenAddressToToken } from '../utils/tokenAddressToToken' +import { formatBNToString } from '../utils/formatBNToString' + +export const bridgeLimitsController = async (req, res) => { + const errors = validationResult(req) + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }) + } + try { + const { fromChain, fromToken, toChain, toToken } = req.query + + const fromTokenInfo = tokenAddressToToken(fromChain, fromToken) + const toTokenInfo = tokenAddressToToken(toChain, toToken) + + const upperLimitValue = parseUnits('1000000', fromTokenInfo.decimals) + const upperLimitBridgeQuotes = await Synapse.allBridgeQuotes( + Number(fromChain), + Number(toChain), + fromTokenInfo.address, + toTokenInfo.address, + upperLimitValue + ) + + const lowerLimitValues = ['0.01', '10'] + let lowerLimitBridgeQuotes = null + + for (const limit of lowerLimitValues) { + const lowerLimitAmount = parseUnits(limit, fromTokenInfo.decimals) + + lowerLimitBridgeQuotes = await Synapse.allBridgeQuotes( + Number(fromChain), + Number(toChain), + fromTokenInfo.address, + toTokenInfo.address, + lowerLimitAmount + ) + + if (lowerLimitBridgeQuotes && lowerLimitBridgeQuotes.length > 0) { + break + } + } + + const maxBridgeAmountQuote = upperLimitBridgeQuotes.reduce( + (maxQuote, currentQuote) => { + const currentMaxAmount = currentQuote.maxAmountOut + const maxAmount = maxQuote ? maxQuote.maxAmountOut : BigNumber.from(0) + + return currentMaxAmount.gt(maxAmount) ? currentQuote : maxQuote + }, + null + ) + + const minBridgeAmountQuote = lowerLimitBridgeQuotes.reduce( + (minQuote, currentQuote) => { + const currentFeeAmount = currentQuote.feeAmount + const minFeeAmount = minQuote ? minQuote.feeAmount : null + + return !minFeeAmount || currentFeeAmount.lt(minFeeAmount) + ? currentQuote + : minQuote + }, + null + ) + + if (!maxBridgeAmountQuote || !minBridgeAmountQuote) { + return res.json({ + maxOriginAmount: null, + minOriginAmount: null, + }) + } + + const maxAmountOriginQueryTokenOutInfo = tokenAddressToToken( + toChain, + maxBridgeAmountQuote.destQuery.tokenOut + ) + + const minAmountOriginQueryTokenOutInfo = tokenAddressToToken( + fromChain, + minBridgeAmountQuote.originQuery.tokenOut + ) + + const maxOriginAmount = formatBNToString( + maxBridgeAmountQuote.maxAmountOut, + maxAmountOriginQueryTokenOutInfo.decimals + ) + + const minOriginAmount = formatBNToString( + minBridgeAmountQuote.feeAmount, + minAmountOriginQueryTokenOutInfo.decimals + ) + + return res.json({ + maxOriginAmount, + minOriginAmount, + }) + } catch (err) { + res.status(500).json({ + error: + 'An unexpected error occurred in /bridgeLimits. 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 index 0a0b4bc7bc..01a56f01ca 100644 --- a/packages/rest-api/src/controllers/bridgeTxInfoController.ts +++ b/packages/rest-api/src/controllers/bridgeTxInfoController.ts @@ -11,8 +11,15 @@ export const bridgeTxInfoController = async (req, res) => { } try { - const { fromChain, toChain, amount, destAddress, fromToken, toToken } = - req.query + const { + fromChain, + toChain, + amount, + destAddress, + fromToken, + toToken, + originUserAddress, + } = req.query const fromTokenInfo = tokenAddressToToken(fromChain.toString(), fromToken) @@ -23,7 +30,10 @@ export const bridgeTxInfoController = async (req, res) => { Number(toChain), fromToken, toToken, - amountInWei + amountInWei, + originUserAddress + ? { originUserAddress: originUserAddress.toString() } + : {} ) const txInfoArray = await Promise.all( diff --git a/packages/rest-api/src/controllers/destinationTxController.ts b/packages/rest-api/src/controllers/destinationTxController.ts index 8db51772a2..bedc02c7f6 100644 --- a/packages/rest-api/src/controllers/destinationTxController.ts +++ b/packages/rest-api/src/controllers/destinationTxController.ts @@ -2,6 +2,7 @@ import { validationResult } from 'express-validator' import { ethers } from 'ethers' import { getTokenDecimals } from '../utils/getTokenDecimals' +import { tokenAddressToToken } from '../utils/tokenAddressToToken' export const destinationTxController = async (req, res) => { const errors = validationResult(req) @@ -47,15 +48,18 @@ export const destinationTxController = async (req, res) => { const toInfo = graphqlData.data.bridgeTransactions[0]?.toInfo || null if (toInfo) { - const { tokenAddress, value, ...restToInfo } = toInfo + const { tokenAddress, value, chainID, ...restToInfo } = toInfo - const tokenDecimals = getTokenDecimals(toInfo.chainID, tokenAddress) + const tokenInfo = tokenAddressToToken(chainID.toString(), tokenAddress) + const tokenDecimals = getTokenDecimals(chainID, tokenAddress) const formattedValue = ethers.utils.formatUnits(value, tokenDecimals) res.json({ status: 'completed', toInfo: { + chainID, ...restToInfo, + tokenSymbol: tokenInfo ? tokenInfo?.symbol : null, formattedValue: `${formattedValue}`, }, }) diff --git a/packages/rest-api/src/middleware/normalizeNativeTokenAddress.ts b/packages/rest-api/src/middleware/normalizeNativeTokenAddress.ts new file mode 100644 index 0000000000..546c08136e --- /dev/null +++ b/packages/rest-api/src/middleware/normalizeNativeTokenAddress.ts @@ -0,0 +1,18 @@ +import { Request, Response, NextFunction } from 'express' +import { isAddress, getAddress } from 'ethers/lib/utils' + +import { NativeGasAddress, ZeroAddress } from '../constants' + +export const normalizeNativeTokenAddress = (addressFields: string[]) => { + return (req: Request, _res: Response, next: NextFunction) => { + for (const field of addressFields) { + const address = req.query[field] + if (typeof address === 'string' && isAddress(address)) { + const checksumAddress = getAddress(address) + req.query[field] = + checksumAddress === ZeroAddress ? NativeGasAddress : checksumAddress + } + } + next() + } +} diff --git a/packages/rest-api/src/routes/bridgeLimitsRoute.ts b/packages/rest-api/src/routes/bridgeLimitsRoute.ts new file mode 100644 index 0000000000..392937ac28 --- /dev/null +++ b/packages/rest-api/src/routes/bridgeLimitsRoute.ts @@ -0,0 +1,146 @@ +import express from 'express' +import { check } from 'express-validator' + +import { CHAINS_ARRAY } from '../constants/chains' +import { showFirstValidationError } from '../middleware/showFirstValidationError' +import { bridgeLimitsController } from '../controllers/bridgeLimitsController' +import { isTokenSupportedOnChain } from './../utils/isTokenSupportedOnChain' +import { isTokenAddress } from '../utils/isTokenAddress' +import { normalizeNativeTokenAddress } from '../middleware/normalizeNativeTokenAddress' +import { checksumAddresses } from '../middleware/checksumAddresses' +import { validateRouteExists } from '../validations/validateRouteExists' + +const router = express.Router() + +/** + * @openapi + * /bridgeLimits: + * get: + * summary: Get min/max origin values for bridge quote + * description: Retrieve min/max bridgeable amounts to bridge from source chain to destination chain. Returns null for min/max amounts if limits are unavailable. + * parameters: + * - in: query + * name: fromChain + * required: true + * schema: + * type: integer + * description: The source chain ID. + * - in: query + * name: toChain + * required: true + * schema: + * type: integer + * description: The destination chain ID. + * - in: query + * name: fromToken + * required: true + * schema: + * type: string + * description: The address of the token on the source chain. + * - in: query + * name: toToken + * required: true + * schema: + * type: string + * description: The address of the token on the destination chain. + * responses: + * 200: + * description: Successful response containing min and max origin amounts. + * content: + * application/json: + * schema: + * type: object + * properties: + * maxOriginAmount: + * type: string + * description: Maximum amount of tokens that can be bridged from the origin chain. + * minOriginAmount: + * type: string + * description: Minimum amount of tokens that can be bridged from the origin chain. + * example: + * maxOriginAmount: "999600" + * minOriginAmount: "4" + * 400: + * description: Invalid input + * content: + * application/json: + * schema: + * type: object + * properties: + * error: + * type: object + * properties: + * value: + * type: string + * message: + * type: string + * field: + * type: string + * location: + * type: string + * example: + * error: + * value: "999" + * message: "Unsupported fromChain" + * field: "fromChain" + * location: "query" + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * error: + * type: string + * details: + * type: string + */ +router.get( + '/', + normalizeNativeTokenAddress(['fromToken', 'toToken']), + checksumAddresses(['fromToken', 'toToken']), + [ + check('fromChain') + .exists() + .withMessage('fromChain is required') + .isNumeric() + .custom((value) => CHAINS_ARRAY.some((c) => c.id === Number(value))) + .withMessage('Unsupported fromChain'), + check('toChain') + .exists() + .withMessage('toChain is required') + .isNumeric() + .custom((value) => CHAINS_ARRAY.some((c) => c.id === Number(value))) + .withMessage('Unsupported toChain'), + check('fromToken') + .exists() + .withMessage('fromToken is required') + .custom((value) => isTokenAddress(value)) + .withMessage('Invalid fromToken address') + .custom((value, { req }) => + isTokenSupportedOnChain(value, req.query.fromChain as string) + ) + .withMessage('Token not supported on specified chain'), + check('toToken') + .exists() + .withMessage('toToken is required') + .custom((value) => isTokenAddress(value)) + .withMessage('Invalid toToken address') + .custom((value, { req }) => + isTokenSupportedOnChain(value, req.query.toChain as string) + ) + .withMessage('Token not supported on specified chain'), + check() + .custom((_value, { req }) => { + const { fromChain, toChain, fromToken, toToken } = req.query + + return validateRouteExists(fromChain, fromToken, toChain, toToken) + }) + .withMessage('No valid route exists for the chain/token combination'), + ], + showFirstValidationError, + bridgeLimitsController +) + +export default router diff --git a/packages/rest-api/src/routes/bridgeRoute.ts b/packages/rest-api/src/routes/bridgeRoute.ts index 3ee858fcb3..5c5d7b6c65 100644 --- a/packages/rest-api/src/routes/bridgeRoute.ts +++ b/packages/rest-api/src/routes/bridgeRoute.ts @@ -1,5 +1,6 @@ import express from 'express' import { check } from 'express-validator' +import { isAddress } from 'ethers/lib/utils' import { isTokenAddress } from '../utils/isTokenAddress' import { CHAINS_ARRAY } from '../constants/chains' @@ -7,6 +8,8 @@ import { showFirstValidationError } from '../middleware/showFirstValidationError import { bridgeController } from '../controllers/bridgeController' import { isTokenSupportedOnChain } from '../utils/isTokenSupportedOnChain' import { checksumAddresses } from '../middleware/checksumAddresses' +import { normalizeNativeTokenAddress } from '../middleware/normalizeNativeTokenAddress' +import { validateRouteExists } from '../validations/validateRouteExists' const router = express.Router() @@ -47,6 +50,12 @@ const router = express.Router() * schema: * type: number * description: The amount of tokens to bridge + * - in: query + * name: originUserAddress + * required: false + * schema: + * type: string + * description: The address of the user on the origin chain * responses: * 200: * description: Successful response @@ -187,6 +196,7 @@ const router = express.Router() */ router.get( '/', + normalizeNativeTokenAddress(['fromToken', 'toToken']), checksumAddresses(['fromToken', 'toToken']), [ check('fromChain') @@ -220,6 +230,17 @@ router.get( ) .withMessage('Token not supported on specified chain'), check('amount').isNumeric().exists().withMessage('amount is required'), + check() + .custom((_value, { req }) => { + const { fromChain, toChain, fromToken, toToken } = req.query + + return validateRouteExists(fromChain, fromToken, toChain, toToken) + }) + .withMessage('No valid route exists for the chain/token combination'), + check('originUserAddress') + .optional() + .custom((value) => isAddress(value)) + .withMessage('Invalid originUserAddress address'), ], showFirstValidationError, bridgeController diff --git a/packages/rest-api/src/routes/bridgeTxInfoRoute.ts b/packages/rest-api/src/routes/bridgeTxInfoRoute.ts index 4336711511..44b6904e08 100644 --- a/packages/rest-api/src/routes/bridgeTxInfoRoute.ts +++ b/packages/rest-api/src/routes/bridgeTxInfoRoute.ts @@ -8,6 +8,8 @@ import { bridgeTxInfoController } from '../controllers/bridgeTxInfoController' import { isTokenAddress } from '../utils/isTokenAddress' import { isTokenSupportedOnChain } from '../utils/isTokenSupportedOnChain' import { checksumAddresses } from '../middleware/checksumAddresses' +import { normalizeNativeTokenAddress } from '../middleware/normalizeNativeTokenAddress' +import { validateRouteExists } from '../validations/validateRouteExists' const router = express.Router() @@ -54,6 +56,12 @@ const router = express.Router() * schema: * type: string * description: The destination address for the bridged tokens + * - in: query + * name: originUserAddress + * required: false + * schema: + * type: string + * description: The address of the user on the origin chain * responses: * 200: * description: Successful response @@ -123,6 +131,7 @@ const router = express.Router() */ router.get( '/', + normalizeNativeTokenAddress(['fromToken', 'toToken']), checksumAddresses(['fromToken', 'toToken']), [ check('fromChain') @@ -161,6 +170,17 @@ router.get( .withMessage('destAddress is required') .custom((value) => isAddress(value)) .withMessage('Invalid destination address'), + check() + .custom((_value, { req }) => { + const { fromChain, toChain, fromToken, toToken } = req.query + + return validateRouteExists(fromChain, fromToken, toChain, toToken) + }) + .withMessage('No valid route exists for the chain/token combination'), + check('originUserAddress') + .optional() + .custom((value) => isAddress(value)) + .withMessage('Invalid originUserAddress address'), ], showFirstValidationError, bridgeTxInfoController diff --git a/packages/rest-api/src/routes/destinationTokensRoute.ts b/packages/rest-api/src/routes/destinationTokensRoute.ts index f06ac9e4f6..a6e4c590d4 100644 --- a/packages/rest-api/src/routes/destinationTokensRoute.ts +++ b/packages/rest-api/src/routes/destinationTokensRoute.ts @@ -8,6 +8,7 @@ import { destinationTokensController } from '../controllers/destinationTokensCon import { isTokenAddress } from '../utils/isTokenAddress' import { isTokenSupportedOnChain } from '../utils/isTokenSupportedOnChain' import { checksumAddresses } from '../middleware/checksumAddresses' +import { normalizeNativeTokenAddress } from '../middleware/normalizeNativeTokenAddress' const router = express.Router() @@ -98,6 +99,7 @@ const router = express.Router() router.get( '/', + normalizeNativeTokenAddress(['fromToken']), checksumAddresses(['fromToken']), [ check('fromChain') diff --git a/packages/rest-api/src/routes/index.ts b/packages/rest-api/src/routes/index.ts index 1bbcf3ea51..2c5e1c547e 100644 --- a/packages/rest-api/src/routes/index.ts +++ b/packages/rest-api/src/routes/index.ts @@ -10,6 +10,7 @@ import bridgeTxStatusRoute from './bridgeTxStatusRoute' import destinationTxRoute from './destinationTxRoute' import tokenListRoute from './tokenListRoute' import destinationTokensRoute from './destinationTokensRoute' +import bridgeLimitsRoute from './bridgeLimitsRoute' const router = express.Router() @@ -18,6 +19,7 @@ router.use('/swap', swapRoute) router.use('/swapTxInfo', swapTxInfoRoute) router.use('/bridge', bridgeRoute) router.use('/bridgeTxInfo', bridgeTxInfoRoute) +router.use('/bridgeLimits', bridgeLimitsRoute) router.use('/synapseTxId', synapseTxIdRoute) router.use('/bridgeTxStatus', bridgeTxStatusRoute) router.use('/destinationTx', destinationTxRoute) diff --git a/packages/rest-api/src/routes/swapRoute.ts b/packages/rest-api/src/routes/swapRoute.ts index ee65c015b0..8c050f3b2b 100644 --- a/packages/rest-api/src/routes/swapRoute.ts +++ b/packages/rest-api/src/routes/swapRoute.ts @@ -7,6 +7,7 @@ import { CHAINS_ARRAY } from '../constants/chains' import { isTokenAddress } from '../utils/isTokenAddress' import { isTokenSupportedOnChain } from '../utils/isTokenSupportedOnChain' import { checksumAddresses } from '../middleware/checksumAddresses' +import { normalizeNativeTokenAddress } from '../middleware/normalizeNativeTokenAddress' const router = express.Router() @@ -133,6 +134,7 @@ const router = express.Router() */ router.get( '/', + normalizeNativeTokenAddress(['fromToken', 'toToken']), checksumAddresses(['fromToken', 'toToken']), [ check('chain') diff --git a/packages/rest-api/src/routes/swapTxInfoRoute.ts b/packages/rest-api/src/routes/swapTxInfoRoute.ts index 2ffb03388f..093dc5686f 100644 --- a/packages/rest-api/src/routes/swapTxInfoRoute.ts +++ b/packages/rest-api/src/routes/swapTxInfoRoute.ts @@ -8,6 +8,7 @@ import { swapTxInfoController } from '../controllers/swapTxInfoController' import { isTokenAddress } from '../utils/isTokenAddress' import { isTokenSupportedOnChain } from '../utils/isTokenSupportedOnChain' import { checksumAddresses } from '../middleware/checksumAddresses' +import { normalizeNativeTokenAddress } from '../middleware/normalizeNativeTokenAddress' const router = express.Router() @@ -115,6 +116,7 @@ const router = express.Router() */ router.get( '/', + normalizeNativeTokenAddress(['fromToken', 'toToken']), checksumAddresses(['fromToken', 'toToken']), [ check('chain') diff --git a/packages/rest-api/src/tests/bridgeLimitsRoute.test.ts b/packages/rest-api/src/tests/bridgeLimitsRoute.test.ts new file mode 100644 index 0000000000..9e68a5d20e --- /dev/null +++ b/packages/rest-api/src/tests/bridgeLimitsRoute.test.ts @@ -0,0 +1,96 @@ +import request from 'supertest' +import express from 'express' + +import bridgeLimitsRoute from '../routes/bridgeLimitsRoute' +import { USDC, ETH } from '../constants/bridgeable' +import { NativeGasAddress } from '../constants' + +const app = express() +app.use('/bridgeLimits', bridgeLimitsRoute) + +describe('Get Bridge Limits Route', () => { + it('should return min/max origin amounts bridging USDC', async () => { + const response = await request(app).get('/bridgeLimits').query({ + fromChain: 1, + fromToken: USDC.addresses[1], + toChain: 10, + toToken: USDC.addresses[10], + }) + + expect(response.status).toBe(200) + expect(response.body).toHaveProperty('maxOriginAmount') + expect(response.body).toHaveProperty('minOriginAmount') + }, 10_000) + + it('should return min/max origin amounts bridging ETH', async () => { + const response = await request(app).get('/bridgeLimits').query({ + fromChain: 1, + fromToken: ETH.addresses[1], + toChain: 10, + toToken: ETH.addresses[10], + }) + + expect(response.status).toBe(200) + expect(response.body).toHaveProperty('maxOriginAmount') + expect(response.body).toHaveProperty('minOriginAmount') + }, 10_000) + + it('should return 400 for unsupported route', async () => { + const response = await request(app).get('/bridgeLimits').query({ + fromChain: '1', + toChain: '10', + fromToken: NativeGasAddress, + toToken: USDC.addresses[10], + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty( + 'message', + 'No valid route exists for the chain/token combination' + ) + }) + + it('should return 400 for unsupported fromChain', async () => { + const response = await request(app).get('/bridgeLimits').query({ + fromChain: '999', + toChain: '137', + fromToken: '0x176211869cA2b568f2A7D4EE941E073a821EE1ff', + toToken: USDC.addresses[137], + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty( + 'message', + 'Unsupported fromChain' + ) + }) + + it('should return 400 for unsupported toChain', async () => { + const response = await request(app).get('/bridgeLimits').query({ + fromChain: '137', + toChain: '999', + fromToken: USDC.addresses[137], + toToken: '0x176211869cA2b568f2A7D4EE941E073a821EE1ff', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('message', 'Unsupported toChain') + }) + + it('should return 400 for missing fromToken', async () => { + const response = await request(app).get('/bridgeLimits').query({ + fromChain: '1', + toChain: '137', + toToken: USDC.addresses[137], + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'fromToken') + }) + + it('should return 400 for missing toToken', async () => { + const response = await request(app).get('/bridgeLimits').query({ + fromChain: '1', + toChain: '137', + fromToken: USDC.addresses[1], + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty('field', 'toToken') + }) +}) diff --git a/packages/rest-api/src/tests/bridgeRoute.test.ts b/packages/rest-api/src/tests/bridgeRoute.test.ts index dcef8451ec..542feab63a 100644 --- a/packages/rest-api/src/tests/bridgeRoute.test.ts +++ b/packages/rest-api/src/tests/bridgeRoute.test.ts @@ -2,6 +2,8 @@ import request from 'supertest' import express from 'express' import bridgeRoute from '../routes/bridgeRoute' +import { NativeGasAddress, ZeroAddress } from '../constants' +import { USDC } from '../constants/bridgeable' const app = express() app.use('/bridge', bridgeRoute) @@ -11,10 +13,28 @@ describe('Bridge Route with Real Synapse Service', () => { const response = await request(app).get('/bridge').query({ fromChain: '1', toChain: '10', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC on Ethereum - toToken: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', // USDC on Optimism + fromToken: USDC.addresses[1], + toToken: USDC.addresses[10], 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 bridge quotes for valid originUserAddress', async () => { + const response = await request(app).get('/bridge').query({ + fromChain: '1', + toChain: '10', + fromToken: USDC.addresses[1], + toToken: USDC.addresses[10], + amount: '1000', + originUserAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', + }) + expect(response.status).toBe(200) expect(Array.isArray(response.body)).toBe(true) expect(response.body.length).toBeGreaterThan(0) @@ -22,6 +42,70 @@ describe('Bridge Route with Real Synapse Service', () => { expect(response.body[0]).toHaveProperty('bridgeFeeFormatted') }, 15000) + it('should return bridge quotes for ZeroAddress', async () => { + const response = await request(app).get('/bridge').query({ + fromChain: '1', + toChain: '10', + fromToken: ZeroAddress, + toToken: ZeroAddress, + amount: '10', + }) + 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 bridge quotes for NativeGasAddress', async () => { + const response = await request(app).get('/bridge').query({ + fromChain: '1', + toChain: '10', + fromToken: NativeGasAddress, + toToken: NativeGasAddress, + amount: '10', + }) + + 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 invalid originUserAddress', async () => { + const response = await request(app).get('/bridge').query({ + fromChain: '1', + toChain: '10', + fromToken: USDC.addresses[1], + toToken: USDC.addresses[10], + amount: '1000', + originUserAddress: 'invalid_address', + }) + + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty( + 'message', + 'Invalid originUserAddress address' + ) + }, 15000) + + it('should return 400 for unsupported route', async () => { + const response = await request(app).get('/bridge').query({ + fromChain: '1', + toChain: '10', + fromToken: NativeGasAddress, + toToken: USDC.addresses[10], + amount: '10', + }) + + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty( + 'message', + 'No valid route exists for the chain/token combination' + ) + }) + it('should return 400 for unsupported fromChain, with error message', async () => { const response = await request(app).get('/bridge').query({ fromChain: '999', @@ -35,7 +119,7 @@ describe('Bridge Route with Real Synapse Service', () => { 'message', 'Unsupported fromChain' ) - }, 10000) + }) it('should return 400 for unsupported toChain, with error message', async () => { const response = await request(app).get('/bridge').query({ @@ -47,7 +131,7 @@ describe('Bridge Route with Real Synapse Service', () => { }) expect(response.status).toBe(400) expect(response.body.error).toHaveProperty('message', 'Unsupported toChain') - }, 10000) + }) it('should return 400 for invalid fromToken address, with error message', async () => { const response = await request(app).get('/bridge').query({ @@ -62,7 +146,7 @@ describe('Bridge Route with Real Synapse Service', () => { 'message', 'Invalid fromToken address' ) - }, 10000) + }) it('should return 400 for token not supported on specified chain, with error message', async () => { const response = await request(app).get('/bridge').query({ @@ -77,16 +161,16 @@ describe('Bridge Route with Real Synapse Service', () => { 'message', 'Invalid fromToken address' ) - }, 10000) + }) it('should return 400 for missing amount, with error message', async () => { const response = await request(app).get('/bridge').query({ fromChain: '1', toChain: '10', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - toToken: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', + fromToken: USDC.addresses[1], + toToken: USDC.addresses[10], }) 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 index 35ff81f19a..e39450e725 100644 --- a/packages/rest-api/src/tests/bridgeTxInfoRoute.test.ts +++ b/packages/rest-api/src/tests/bridgeTxInfoRoute.test.ts @@ -2,6 +2,8 @@ import request from 'supertest' import express from 'express' import bridgeTxInfoRoute from '../routes/bridgeTxInfoRoute' +import { USDC } from '../constants/bridgeable' +import { NativeGasAddress } from '../constants' const app = express() app.use('/bridgeTxInfo', bridgeTxInfoRoute) @@ -11,8 +13,8 @@ describe('Bridge TX Info Route', () => { const response = await request(app).get('/bridgeTxInfo').query({ fromChain: '1', toChain: '137', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC on Ethereum - toToken: '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', // USDC on Polygon + fromToken: USDC.addresses[1], + toToken: USDC.addresses[137], amount: '1000', destAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', }) @@ -27,6 +29,61 @@ describe('Bridge TX Info Route', () => { ) }, 10_000) + it('should return bridge transaction info for valid input with valid originUserAddress', async () => { + const response = await request(app).get('/bridgeTxInfo').query({ + fromChain: '1', + toChain: '137', + fromToken: USDC.addresses[1], + toToken: USDC.addresses[137], + amount: '1000', + destAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', + originUserAddress: '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 invalid originUserAddress', async () => { + const response = await request(app).get('/bridgeTxInfo').query({ + fromChain: '1', + toChain: '137', + fromToken: USDC.addresses[1], + toToken: USDC.addresses[137], + amount: '1000', + destAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', + originUserAddress: 'invalid_address', + }) + + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty( + 'message', + 'Invalid originUserAddress address' + ) + }, 10_000) + + it('should return 400 for unsupported route', async () => { + const response = await request(app).get('/bridgeTxInfo').query({ + fromChain: '1', + toChain: '10', + fromToken: NativeGasAddress, + toToken: USDC.addresses[10], + amount: '10', + destAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', + }) + expect(response.status).toBe(400) + expect(response.body.error).toHaveProperty( + 'message', + 'No valid route exists for the chain/token combination' + ) + }) + it('should return 400 for unsupported fromChain', async () => { const response = await request(app).get('/bridgeTxInfo').query({ fromChain: '999', @@ -41,7 +98,7 @@ describe('Bridge TX Info Route', () => { 'message', 'Unsupported fromChain' ) - }, 10_000) + }) it('should return 400 for invalid fromToken address', async () => { const response = await request(app).get('/bridgeTxInfo').query({ @@ -57,7 +114,7 @@ describe('Bridge TX Info Route', () => { 'message', 'Invalid fromToken address' ) - }, 10_000) + }) it('should return 400 for token not supported on specified chain', async () => { const response = await request(app).get('/bridgeTxInfo').query({ @@ -73,26 +130,26 @@ describe('Bridge TX Info Route', () => { 'message', 'Invalid fromToken address' ) - }, 10_000) + }) it('should return 400 for missing amount', async () => { const response = await request(app).get('/bridgeTxInfo').query({ fromChain: '1', toChain: '137', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - toToken: '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', + fromToken: USDC.addresses[1], + toToken: USDC.addresses[137], destAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', }) expect(response.status).toBe(400) expect(response.body.error).toHaveProperty('field', 'amount') - }, 10_000) + }) it('should return 400 for invalid destAddress', async () => { const response = await request(app).get('/bridgeTxInfo').query({ fromChain: '1', toChain: '137', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - toToken: '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', + fromToken: USDC.addresses[1], + toToken: USDC.addresses[137], amount: '1000', destAddress: 'invalid_address', }) @@ -101,5 +158,5 @@ describe('Bridge TX Info Route', () => { 'message', 'Invalid destination address' ) - }, 10_000) + }) }) diff --git a/packages/rest-api/src/tests/destinationTokensRoute.test.ts b/packages/rest-api/src/tests/destinationTokensRoute.test.ts index 4fe40a7a62..a083741ffa 100644 --- a/packages/rest-api/src/tests/destinationTokensRoute.test.ts +++ b/packages/rest-api/src/tests/destinationTokensRoute.test.ts @@ -2,6 +2,8 @@ import request from 'supertest' import express from 'express' import destinationTokensRoute from '../routes/destinationTokensRoute' +import { NativeGasAddress, ZeroAddress } from '../constants' +import { USDC, USDT } from '../constants/bridgeable' const app = express() app.use('/destinationTokens', destinationTokensRoute) @@ -10,7 +12,7 @@ describe('destinatonTokens Route', () => { it('should return destination tokens for valid input', async () => { const response = await request(app).get('/destinationTokens').query({ fromChain: '1', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + fromToken: USDC.addresses[1], }) expect(response.status).toBe(200) @@ -24,7 +26,21 @@ describe('destinatonTokens Route', () => { it('should return destination tokens for valid gas Tokens', async () => { const response = await request(app).get('/destinationTokens').query({ fromChain: '1', - fromToken: '0x0000000000000000000000000000000000000000', + fromToken: NativeGasAddress, + }) + + expect(response.status).toBe(200) + expect(Array.isArray(response.body)).toBe(true) + expect(response.body.length).toBeGreaterThan(0) + expect(response.body[0]).toHaveProperty('symbol') + expect(response.body[0]).toHaveProperty('address') + expect(response.body[0]).toHaveProperty('chainId') + }) + + it('should return destination tokens for valid gas Tokens, ZeroAddress', async () => { + const response = await request(app).get('/destinationTokens').query({ + fromChain: '1', + fromToken: ZeroAddress, }) expect(response.status).toBe(200) @@ -40,7 +56,7 @@ describe('destinatonTokens Route', () => { const response = await request(app).get('/destinationTokens').query({ fromChain: '534352', - fromToken: '0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4', + fromToken: USDC.addresses[534352], }) expect(response.status).toBe(200) @@ -54,7 +70,7 @@ describe('destinatonTokens Route', () => { it('should return destination tokens for non-checksummed address', async () => { const response = await request(app).get('/destinationTokens').query({ fromChain: '43114', - fromToken: '0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7', + fromToken: USDT.addresses[43114].toLowerCase(), }) expect(response.status).toBe(200) @@ -107,7 +123,7 @@ describe('destinatonTokens Route', () => { it('should return 400 for token not supported on specified chain', async () => { const response = await request(app).get('/destinationTokens').query({ fromChain: '10', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + fromToken: USDC.addresses[1], }) expect(response.status).toBe(400) @@ -119,7 +135,7 @@ describe('destinatonTokens Route', () => { it('should return 400 for missing fromChain', async () => { const response = await request(app).get('/destinationTokens').query({ - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + fromToken: USDC.addresses[1], }) expect(response.status).toBe(400) diff --git a/packages/rest-api/src/tests/swapRoute.test.ts b/packages/rest-api/src/tests/swapRoute.test.ts index 6c6d7ac43b..775372bc31 100644 --- a/packages/rest-api/src/tests/swapRoute.test.ts +++ b/packages/rest-api/src/tests/swapRoute.test.ts @@ -2,6 +2,8 @@ import request from 'supertest' import express from 'express' import swapRoute from '../routes/swapRoute' +import { NativeGasAddress, ZeroAddress } from '../constants' +import { DAI, NETH, USDC } from '../constants/bridgeable' const app = express() app.use('/swap', swapRoute) @@ -10,8 +12,8 @@ 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: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC on Ethereum - toToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI on Ethereum + fromToken: USDC.addresses[1], + toToken: DAI.addresses[1], amount: '1000', }) @@ -21,11 +23,39 @@ describe('Swap Route with Real Synapse Service', () => { expect(response.body).toHaveProperty('query') }, 10_000) + it('should return a real swap quote for valid input, Eth ZeroAddress', async () => { + const response = await request(app).get('/swap').query({ + chain: '10', + fromToken: ZeroAddress, + toToken: NETH.addresses[10], + amount: '1', + }) + + 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 a real swap quote for valid input, Eth NativeGasAddress', async () => { + const response = await request(app).get('/swap').query({ + chain: '10', + fromToken: NativeGasAddress, + toToken: NETH.addresses[10], + amount: '1', + }) + + 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: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - toToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + fromToken: USDC.addresses[1], + toToken: DAI.addresses[1], amount: '1000', }) @@ -36,7 +66,7 @@ describe('Swap Route with Real Synapse Service', () => { it('should return 400 for invalid toToken address, with error message', async () => { const response = await request(app).get('/swap').query({ chain: '1', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + fromToken: USDC.addresses[1], toToken: 'invalid_address', amount: '1000', }) @@ -51,7 +81,7 @@ describe('Swap Route with Real Synapse Service', () => { it('should return 400 for token not supported on specified chain', async () => { const response = await request(app).get('/swap').query({ chain: '1', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + fromToken: USDC.addresses[1], toToken: '0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F', // SNX on Ethereum (Not supported) amount: '1000', }) @@ -66,8 +96,8 @@ describe('Swap Route with Real Synapse Service', () => { it('should return 400 for missing amount, with error message', async () => { const response = await request(app).get('/swap').query({ chain: '1', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - toToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + fromToken: USDC.addresses[1], + toToken: DAI.addresses[1], }) expect(response.status).toBe(400) diff --git a/packages/rest-api/src/tests/swapTxInfoRoute.test.ts b/packages/rest-api/src/tests/swapTxInfoRoute.test.ts index 3faf9ca8ea..99ea0e0f42 100644 --- a/packages/rest-api/src/tests/swapTxInfoRoute.test.ts +++ b/packages/rest-api/src/tests/swapTxInfoRoute.test.ts @@ -2,6 +2,7 @@ import request from 'supertest' import express from 'express' import swapTxInfoRoute from '../routes/swapTxInfoRoute' +import { DAI, USDC } from '../constants/bridgeable' const app = express() app.use('/swapTxInfo', swapTxInfoRoute) @@ -10,8 +11,8 @@ 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: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC on Ethereum - toToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI on Ethereum + fromToken: USDC.addresses[1], + toToken: DAI.addresses[1], amount: '1000', address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', }) @@ -24,8 +25,8 @@ describe('Swap TX Info Route with Real Synapse Service', () => { it('should return 400 for invalid address, with error message', async () => { const response = await request(app).get('/swapTxInfo').query({ chain: '1', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - toToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + fromToken: USDC.addresses[1], + toToken: DAI.addresses[1], amount: '1000', address: 'invalid_address', }) @@ -39,8 +40,8 @@ describe('Swap TX Info Route with Real Synapse Service', () => { it('should return 400 for unsupported chain, with error message', async () => { const response = await request(app).get('/swapTxInfo').query({ chain: '111', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - toToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + fromToken: USDC.addresses[1], + toToken: DAI.addresses[1], amount: '1000', address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', }) @@ -51,7 +52,7 @@ describe('Swap TX Info Route with Real Synapse Service', () => { it('should return 400 for invalid toToken address, with error message', async () => { const response = await request(app).get('/swapTxInfo').query({ chain: '1', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + fromToken: USDC.addresses[1], toToken: 'invalid_address', amount: '1000', address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', @@ -66,7 +67,7 @@ describe('Swap TX Info Route with Real Synapse Service', () => { it('should return 400 for token not supported on specified chain', async () => { const response = await request(app).get('/swapTxInfo').query({ chain: '1', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + fromToken: USDC.addresses[1], toToken: '0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F', // SNX on Ethereum (Not supported) amount: '1000', address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', @@ -81,8 +82,8 @@ describe('Swap TX Info Route with Real Synapse Service', () => { it('should return 400 for missing amount, with error message', async () => { const response = await request(app).get('/swapTxInfo').query({ chain: '1', - fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - toToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + fromToken: USDC.addresses[1], + toToken: DAI.addresses[1], address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', }) expect(response.status).toBe(400) diff --git a/packages/rest-api/src/tests/tokenListRoute.test.ts b/packages/rest-api/src/tests/tokenListRoute.test.ts index 16d2fb82df..127ea9fb12 100644 --- a/packages/rest-api/src/tests/tokenListRoute.test.ts +++ b/packages/rest-api/src/tests/tokenListRoute.test.ts @@ -16,7 +16,7 @@ describe('Index Route', () => { expect(keys.length).toBe(62) expect(response.body['ETH']['addresses']['1']).toBe( - '0x0000000000000000000000000000000000000000' + '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' ) expect(response.body['SYN']['addresses']['1']).toBe( '0x0f2d719407fdbeff09d87557abb7232601fd9f29' diff --git a/packages/rest-api/src/utils/bridgeRouteMapping.ts b/packages/rest-api/src/utils/bridgeRouteMapping.ts index 996652e543..992ccffe0f 100644 --- a/packages/rest-api/src/utils/bridgeRouteMapping.ts +++ b/packages/rest-api/src/utils/bridgeRouteMapping.ts @@ -13,7 +13,7 @@ type TransformedBridgeRoutes = Record const constructJSON = ( swappableMap, exclusionList -): TransformedBridgeRoutes => { +): StringifiedBridgeRoutes => { const result = {} // Iterate through the chains @@ -56,8 +56,7 @@ const constructJSON = ( } } } - - return transformBridgeRouteValues(result) + return result } const transformPair = (string: string): any => { @@ -97,4 +96,7 @@ const transformBridgeRouteValues = ( ) } -export const BRIDGE_ROUTE_MAPPING = constructJSON(BRIDGE_MAP, []) +export const BRIDGE_ROUTE_MAPPING_SYMBOLS = constructJSON(BRIDGE_MAP, []) +export const BRIDGE_ROUTE_MAPPING = transformBridgeRouteValues( + BRIDGE_ROUTE_MAPPING_SYMBOLS +) diff --git a/packages/rest-api/src/utils/tokenAddressToToken.ts b/packages/rest-api/src/utils/tokenAddressToToken.ts index 21c073e7d5..6bad892582 100644 --- a/packages/rest-api/src/utils/tokenAddressToToken.ts +++ b/packages/rest-api/src/utils/tokenAddressToToken.ts @@ -1,4 +1,3 @@ -import { NativeGasAddress, ZeroAddress } from '../constants' import { BRIDGE_MAP } from '../constants/bridgeMap' export const tokenAddressToToken = (chain: string, tokenAddress: string) => { @@ -7,12 +6,12 @@ export const tokenAddressToToken = (chain: string, tokenAddress: string) => { return null } - const address = tokenAddress === ZeroAddress ? NativeGasAddress : tokenAddress + const tokenInfo = chainData[tokenAddress] - const tokenInfo = chainData[address] if (!tokenInfo) { return null } + return { address: tokenAddress, symbol: tokenInfo.symbol, diff --git a/packages/rest-api/src/validations/validateRouteExists.ts b/packages/rest-api/src/validations/validateRouteExists.ts new file mode 100644 index 0000000000..4339d9d8c1 --- /dev/null +++ b/packages/rest-api/src/validations/validateRouteExists.ts @@ -0,0 +1,20 @@ +import { tokenAddressToToken } from '../utils/tokenAddressToToken' +import { BRIDGE_ROUTE_MAPPING_SYMBOLS } from '../utils/bridgeRouteMapping' + +export const validateRouteExists = (fromChain, fromToken, toChain, toToken) => { + const fromTokenInfo = tokenAddressToToken(fromChain.toString(), fromToken) + const toTokenInfo = tokenAddressToToken(toChain.toString(), toToken) + + if (!fromTokenInfo || !toTokenInfo) { + return false + } + + const key = `${fromTokenInfo.symbol}-${fromChain}` + const routes = BRIDGE_ROUTE_MAPPING_SYMBOLS[key] + + if (!routes) { + return false + } + + return routes.includes(`${toTokenInfo.symbol}-${toChain}`) +} diff --git a/packages/rest-api/src/validations/validateTokens.ts b/packages/rest-api/src/validations/validateTokens.ts deleted file mode 100644 index 6e89a9c70c..0000000000 --- a/packages/rest-api/src/validations/validateTokens.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { check } from 'express-validator' - -import { tokenSymbolToToken } from '../utils/tokenSymbolToToken' - -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 = tokenSymbolToToken(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/rfq-indexer/README.md b/packages/rfq-indexer/README.md index 120c08c78d..6d70eb8e37 100644 --- a/packages/rfq-indexer/README.md +++ b/packages/rfq-indexer/README.md @@ -12,14 +12,15 @@ The RFQ (Request for Quote) Indexer is a system designed to index and track brid - API: Used by front-end applications, other services, or developers to query the indexed data. ## Directory Structure -rfq-indexer/ -├── api/ # API service -│ ├── src/ # API source code -│ ├── package.json # API dependencies and scripts -│ └── README.md # API documentation -├── indexer/ # Indexer service -│ ├── src/ # Indexer source code -│ ├── abis/ # Contract ABIs -│ ├── package.json # Indexer dependencies and scripts -│ └── README.md # Indexer documentation -└── README.md # This file +

+rfq-indexer
+├── api: API service
+│   ├── src/ : API source code
+│   ├── package.json : API dependencies and scripts
+│   ├── README.md : API documentation
+├── indexer: Indexer service
+│   ├── src/ : Indexer source code
+│   ├── abis/ : Contract ABIs
+│   ├── package.json : Indexer dependencies and scripts
+│   ├── README.md : Indexer documentation
+
diff --git a/packages/rfq-indexer/api/CHANGELOG.md b/packages/rfq-indexer/api/CHANGELOG.md index 748a06cf8b..2ffd88a1c8 100644 --- a/packages/rfq-indexer/api/CHANGELOG.md +++ b/packages/rfq-indexer/api/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. +## [1.0.4](https://github.com/synapsecns/sanguine/compare/@synapsecns/rfq-indexer-api@1.0.3...@synapsecns/rfq-indexer-api@1.0.4) (2024-09-22) + +**Note:** Version bump only for package @synapsecns/rfq-indexer-api + + + + + ## [1.0.3](https://github.com/synapsecns/sanguine/compare/@synapsecns/rfq-indexer-api@1.0.2...@synapsecns/rfq-indexer-api@1.0.3) (2024-09-20) **Note:** Version bump only for package @synapsecns/rfq-indexer-api diff --git a/packages/rfq-indexer/api/package.json b/packages/rfq-indexer/api/package.json index dda045192c..ca054661f8 100644 --- a/packages/rfq-indexer/api/package.json +++ b/packages/rfq-indexer/api/package.json @@ -1,22 +1,23 @@ { "name": "@synapsecns/rfq-indexer-api", "private": true, - "version": "1.0.3", + "version": "1.0.4", "description": "", "main": "index.js", "scripts": { "check-env": "dotenv -e .env.local -- printenv | grep DATABASE_URL", - "dev:local": "dotenv -e .env.local -- tsx watch src/index.ts", - "dev:prod": "dotenv -e .env.production -- tsx watch src/index.ts", - "start": "tsx src/index.ts", - "start:local": "dotenv -e .env -- tsx src/index.ts", - "dev": "dotenv -e .env -- tsx watch src/index.ts", + "dev:local": "dotenv -e .env.local -- tsx watch src/app.ts", + "dev:prod": "dotenv -e .env.production -- tsx watch src/app.ts", + "start": "tsx src/app.ts", + "start:local": "dotenv -e .env -- tsx src/app.ts", + "dev": "dotenv -e .env -- tsx watch src/app.ts", "lint:check": " ", "ci:lint": " ", "build:go": " ", "build": " ", "build:slither": " ", - "test:coverage": "echo 'No tests defined.'" + "test": "", + "test:coverage": "echo no tests defined" }, "keywords": [], "author": "", @@ -29,10 +30,12 @@ "@types/node": "^22.5.4", "dotenv-cli": "^7.4.2", "express": "^4.21.0", + "express-validator": "^7.2.0", "graphql": "^16.9.0", "graphql-yoga": "^5.7.0", "kysely": "^0.27.4", "pg": "^8.12.0", + "supertest": "^7.0.0", "ts-node": "^10.9.2", "tsx": "^4.19.1", "typescript": "^5.6.2", @@ -42,8 +45,17 @@ "node": ">=18.17" }, "devDependencies": { + "@babel/core": "^7.25.2", + "@babel/preset-env": "^7.25.4", + "@babel/preset-typescript": "^7.24.7", "@types/pg": "^8.11.9", - "dotenv": "^16.4.5" + "@types/supertest": "^6.0.2", + "@types/swagger-jsdoc": "6.0.4", + "@types/swagger-ui-express": "4.1.6", + "dotenv": "^16.4.5", + "express-validator": "^7.2.0", + "swagger-jsdoc": "^6.2.8", + "swagger-ui-express": "^5.0.1" }, "repository": { "type": "git", diff --git a/packages/rfq-indexer/api/src/.babelrc b/packages/rfq-indexer/api/src/.babelrc new file mode 100644 index 0000000000..3313ff9ef0 --- /dev/null +++ b/packages/rfq-indexer/api/src/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@babel/preset-env", "@babel/preset-typescript"] +} diff --git a/packages/rfq-indexer/api/src/app.ts b/packages/rfq-indexer/api/src/app.ts new file mode 100644 index 0000000000..4209656836 --- /dev/null +++ b/packages/rfq-indexer/api/src/app.ts @@ -0,0 +1,32 @@ +import express from 'express' +import swaggerUi from 'swagger-ui-express' +import { createYoga } from 'graphql-yoga' + +import { specs } from './swagger' +import routes from './routes' +import { schema } from './graphql/schema' +import { overrideJsonBigIntSerialization } from './utils/overrideJsonBigIntSerialization' + +const app = express() +const port = process.env.PORT || 3001 + +overrideJsonBigIntSerialization() + +app.use(express.json()) + +// Swagger UI setup +app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs)) + +// REST API routes +app.use('/api', routes) + +// GraphQL setup +const yoga = createYoga({ schema }) +app.use('/graphql', yoga) + +export const server = app.listen(port, () => { + console.log(`Server listening at ${port}`) + console.info('API server runs on http://localhost:3001') + console.info('REST requests go through http://localhost:3001/api') + console.info('GraphQL requests go through http://localhost:3001/graphql') +}) diff --git a/packages/rfq-indexer/api/src/controllers/conflictingProofsController.ts b/packages/rfq-indexer/api/src/controllers/conflictingProofsController.ts new file mode 100644 index 0000000000..5079bb0d5f --- /dev/null +++ b/packages/rfq-indexer/api/src/controllers/conflictingProofsController.ts @@ -0,0 +1,50 @@ +import { Request, Response } from 'express' +import { sql } from 'kysely' + +import { db } from '../db' +import { qDeposits, qRelays, qProofs } from '../queries' +import { nest_results } from '../utils/nestResults' + +export const conflictingProofsController = async ( + req: Request, + res: Response +) => { + try { + const query = db + .with('deposits', () => qDeposits()) + .with('relays', () => qRelays()) + .with('proofs', () => qProofs()) + .with('combined', (qb) => + qb + .selectFrom('deposits') + .leftJoin('relays', 'transactionId_deposit', 'transactionId_relay') + .leftJoin('proofs', 'transactionId_deposit', 'transactionId_proof') + .selectAll('deposits') + .selectAll('relays') + .selectAll('proofs') + ) + .selectFrom('combined') + .selectAll() + .where('relayer_proof', 'is not', null) + .where('relayer_relay', 'is not', null) + .where( + (eb) => + sql`LOWER(${eb.ref('relayer_relay')}) != LOWER(${eb.ref( + 'relayer_proof' + )})` + ) + .orderBy('blockTimestamp_proof', 'desc') + + const results = await query.execute() + const conflictingProofs = nest_results(results) + + if (conflictingProofs && conflictingProofs.length > 0) { + res.json(conflictingProofs) + } else { + res.status(200).json({ message: 'No conflicting proofs found' }) + } + } catch (error) { + console.error('Error fetching conflicting proofs:', error) + res.status(500).json({ message: 'Internal server error' }) + } +} diff --git a/packages/rfq-indexer/api/src/controllers/invalidRelaysController.ts b/packages/rfq-indexer/api/src/controllers/invalidRelaysController.ts new file mode 100644 index 0000000000..4d17a56711 --- /dev/null +++ b/packages/rfq-indexer/api/src/controllers/invalidRelaysController.ts @@ -0,0 +1,52 @@ +import { Request, Response } from 'express' + +import { db } from '../db' + +export const recentInvalidRelaysController = async ( + req: Request, + res: Response +) => { + try { + const query = db + .selectFrom('BridgeRelayedEvents') + .leftJoin( + 'BridgeRequestEvents', + 'BridgeRelayedEvents.transactionId', + 'BridgeRequestEvents.transactionId' + ) + .select([ + 'BridgeRelayedEvents.transactionId', + 'BridgeRelayedEvents.blockNumber', + 'BridgeRelayedEvents.blockTimestamp', + 'BridgeRelayedEvents.transactionHash', + 'BridgeRelayedEvents.originChain', + 'BridgeRelayedEvents.destChain', + 'BridgeRelayedEvents.originChainId', + 'BridgeRelayedEvents.destChainId', + 'BridgeRelayedEvents.originToken', + 'BridgeRelayedEvents.destToken', + 'BridgeRelayedEvents.originAmountFormatted', + 'BridgeRelayedEvents.destAmountFormatted', + 'BridgeRelayedEvents.to', + 'BridgeRelayedEvents.relayer', + ]) + // lookback approx 2 weeks + .where( + 'BridgeRelayedEvents.blockTimestamp', + '>', + Math.floor(Date.now() / 1000) - 2 * 7 * 24 * 60 * 60 + ) + .where('BridgeRequestEvents.transactionId', 'is', null) + + const results = await query.execute() + + if (results && results.length > 0) { + res.json(results) + } else { + res.status(200).json({ message: 'No recent invalid relays found' }) + } + } catch (error) { + console.error('Error fetching recent invalid relays:', error) + res.status(500).json({ message: 'Internal server error' }) + } +} diff --git a/packages/rfq-indexer/api/src/controllers/pendingTransactionsController.ts b/packages/rfq-indexer/api/src/controllers/pendingTransactionsController.ts new file mode 100644 index 0000000000..dfef98c11e --- /dev/null +++ b/packages/rfq-indexer/api/src/controllers/pendingTransactionsController.ts @@ -0,0 +1,129 @@ +import { Request, Response } from 'express' + +import { db } from '../db' +import { qDeposits, qRelays, qProofs, qClaims, qRefunds } from '../queries' +import { nest_results } from '../utils/nestResults' + +export const pendingTransactionsMissingClaimController = async ( + req: Request, + res: Response +) => { + try { + const query = db + .with('deposits', () => qDeposits()) + .with('relays', () => qRelays()) + .with('proofs', () => qProofs()) + .with('claims', () => qClaims()) + .with('combined', (qb) => + qb + .selectFrom('deposits') + .innerJoin('relays', 'transactionId_deposit', 'transactionId_relay') + .innerJoin('proofs', 'transactionId_deposit', 'transactionId_proof') + .leftJoin('claims', 'transactionId_deposit', 'transactionId_claim') + .selectAll('deposits') + .selectAll('relays') + .selectAll('proofs') + .where('transactionId_claim', 'is', null) + ) + .selectFrom('combined') + .selectAll() + .orderBy('blockTimestamp_proof', 'desc') + + const results = await query.execute() + const nestedResults = nest_results(results) + + if (nestedResults && nestedResults.length > 0) { + res.json(nestedResults) + } else { + res + .status(404) + .json({ message: 'No pending transactions missing claim found' }) + } + } catch (error) { + console.error('Error fetching pending transactions missing claim:', error) + res.status(500).json({ message: 'Internal server error' }) + } +} + + +export const pendingTransactionsMissingProofController = async ( + req: Request, + res: Response +) => { + try { + const query = db + .with('deposits', () => qDeposits()) + .with('relays', () => qRelays()) + .with('proofs', () => qProofs()) + .with('combined', (qb) => + qb + .selectFrom('deposits') + .innerJoin('relays', 'transactionId_deposit', 'transactionId_relay') + .leftJoin('proofs', 'transactionId_deposit', 'transactionId_proof') + .selectAll('deposits') + .selectAll('relays') + .where('transactionId_proof', 'is', null) + ) + .selectFrom('combined') + .selectAll() + .orderBy('blockTimestamp_relay', 'desc') + + const results = await query.execute() + const nestedResults = nest_results(results) + + if (nestedResults && nestedResults.length > 0) { + res.json(nestedResults) + } else { + res + .status(404) + .json({ message: 'No pending transactions missing proof found' }) + } + } catch (error) { + console.error('Error fetching pending transactions missing proof:', error) + res.status(500).json({ message: 'Internal server error' }) + } +} + +export const pendingTransactionsMissingRelayController = async ( + req: Request, + res: Response +) => { + try { + const query = db + .with('deposits', () => qDeposits()) + .with('relays', () => qRelays()) + .with('refunds', () => qRefunds()) + .with( + 'combined', + (qb) => + qb + .selectFrom('deposits') + .selectAll('deposits') + .leftJoin('relays', 'transactionId_deposit', 'transactionId_relay') + .leftJoin( + 'refunds', + 'transactionId_deposit', + 'transactionId_refund' + ) + .where('transactionId_relay', 'is', null) // is not relayed + .where('transactionId_refund', 'is', null) // is not refunded + ) + .selectFrom('combined') + .selectAll() + .orderBy('blockTimestamp_deposit', 'desc') + + const results = await query.execute() + const nestedResults = nest_results(results) + + if (nestedResults && nestedResults.length > 0) { + res.json(nestedResults) + } else { + res + .status(404) + .json({ message: 'No pending transactions missing relay found' }) + } + } catch (error) { + console.error('Error fetching pending transactions missing relay:', error) + res.status(500).json({ message: 'Internal server error' }) + } +} diff --git a/packages/rfq-indexer/api/src/controllers/refundedAndRelayedController.ts b/packages/rfq-indexer/api/src/controllers/refundedAndRelayedController.ts new file mode 100644 index 0000000000..dd9fb2f0af --- /dev/null +++ b/packages/rfq-indexer/api/src/controllers/refundedAndRelayedController.ts @@ -0,0 +1,43 @@ +import { Request, Response } from 'express' + +import { db } from '../db' +import { qDeposits, qRelays, qRefunds } from '../queries' +import { nest_results } from '../utils/nestResults' + +export const refundedAndRelayedTransactionsController = async ( + req: Request, + res: Response +) => { + try { + const query = db + .with('deposits', () => qDeposits()) + .with('relays', () => qRelays()) + .with('refunds', () => qRefunds()) + .with('combined', (qb) => + qb + .selectFrom('deposits') + .innerJoin('relays', 'transactionId_deposit', 'transactionId_relay') + .innerJoin('refunds', 'transactionId_deposit', 'transactionId_refund') + .selectAll('deposits') + .selectAll('relays') + .selectAll('refunds') + ) + .selectFrom('combined') + .selectAll() + .orderBy('blockTimestamp_refund', 'desc') + + const results = await query.execute() + const nestedResults = nest_results(results) + + if (nestedResults && nestedResults.length > 0) { + res.json(nestedResults) + } else { + res + .status(200) + .json({ message: 'No refunded and relayed transactions found' }) + } + } catch (error) { + console.error('Error fetching refunded and relayed transactions:', error) + res.status(500).json({ message: 'Internal server error' }) + } +} diff --git a/packages/rfq-indexer/api/src/controllers/transactionIdController.ts b/packages/rfq-indexer/api/src/controllers/transactionIdController.ts new file mode 100644 index 0000000000..73857496fa --- /dev/null +++ b/packages/rfq-indexer/api/src/controllers/transactionIdController.ts @@ -0,0 +1,58 @@ +import { Request, Response } from 'express' + +import { db } from '../db' +import { qDeposits, qRelays, qProofs, qClaims, qRefunds } from '../queries' +import { nest_results } from '../utils/nestResults' + +export const getTransactionById = async (req: Request, res: Response) => { + const { transactionId } = req.params + + try { + const query = db + .with('deposits', () => + qDeposits().where('transactionId', '=', transactionId as string) + ) + .with('relays', () => qRelays()) + .with('proofs', () => qProofs()) + .with('claims', () => qClaims()) + .with('refunds', () => qRefunds()) + .with('combined', (qb) => + qb + .selectFrom('deposits') + .leftJoin('relays', 'transactionId_deposit', 'transactionId_relay') + .leftJoin('proofs', 'transactionId_deposit', 'transactionId_proof') + .leftJoin('claims', 'transactionId_deposit', 'transactionId_claim') + .leftJoin('refunds', 'transactionId_deposit', 'transactionId_refund') + .selectAll('deposits') + .selectAll('relays') + .selectAll('proofs') + .selectAll('claims') + .selectAll('refunds') + ) + .selectFrom('combined') + .selectAll() + + const results = await query.execute() + const nestedResult = nest_results(results)[0] || null + + if (nestedResult) { + const filteredResult = Object.fromEntries( + Object.entries(nestedResult).filter(([_, value]) => { + if (value === null) { + return false + } + if (typeof value !== 'object') { + return true + } + return Object.values(value).some((v) => v !== null) + }) + ) + res.json(filteredResult) + } else { + res.status(200).json({ message: 'Transaction not found' }) + } + } catch (error) { + console.error(error) + res.status(500).json({ message: 'Internal server error' }) + } +} diff --git a/packages/rfq-indexer/api/src/index.ts b/packages/rfq-indexer/api/src/index.ts deleted file mode 100644 index f64c412c84..0000000000 --- a/packages/rfq-indexer/api/src/index.ts +++ /dev/null @@ -1,100 +0,0 @@ -import express from 'express' -import { createYoga } from 'graphql-yoga' - -import { schema } from './graphql/schema' -import { overrideJsonBigIntSerialization } from './utils/overrideJsonBigIntSerialization' -import { resolvers } from './graphql/resolvers' - -overrideJsonBigIntSerialization() - -const app = express() - -const yoga = createYoga({ schema }) - -app.use(yoga.graphqlEndpoint, yoga) - -app.get('/api/hello', (req, res) => { - res.json({ message: 'Hello World!' }) -}) - -app.get('/api/pending-transactions-missing-relay', async (req, res) => { - try { - const pendingTransactions = - await resolvers.Query.pendingTransactionsMissingRelay() - res.json(pendingTransactions) - } catch (error) { - console.error('Error fetching pending transactions missing relay:', error) - res.status(500).json({ error: 'Internal server error' }) - } -}) - -app.get('/api/pending-transactions-missing-proof', async (req, res) => { - try { - const pendingTransactionsMissingProof = - await resolvers.Query.pendingTransactionsMissingProof() - res.json(pendingTransactionsMissingProof) - } catch (error) { - console.error('Error fetching pending transactions missing proof:', error) - res.status(500).json({ error: 'Internal server error' }) - } -}) - -app.get('/api/pending-transactions-missing-claim', async (req, res) => { - try { - const pendingTransactionsMissingClaim = - await resolvers.Query.pendingTransactionsMissingClaim() - res.json(pendingTransactionsMissingClaim) - } catch (error) { - console.error('Error fetching pending transactions missing claim:', error) - res.status(500).json({ error: 'Internal server error' }) - } -}) - -app.get('/api/recent-invalid-relays', async (req, res) => { - try { - const queryResult = await resolvers.Query.recentInvalidRelays() - res.json(queryResult) - } catch (error) { - console.error('Error fetching recent invalid relays:', error) - res.status(500).json({ error: 'Internal server error' }) - } -}) - -app.get('/api/conflicting-proofs', async (req, res) => { - try { - const conflictingProofs = await resolvers.Query.conflictingProofs() - res.json(conflictingProofs) - } catch (error) { - console.error('Error fetching conflicting proofs:', error) - res.status(500).json({ error: 'Internal server error' }) - } -}) - -app.get('/api/transaction/:transactionId', async (req, res) => { - try { - const transactionId = req.params.transactionId - const transaction = await resolvers.Query.transactionById(null, { - transactionId, - }) - res.json(transaction) - } catch (error) { - console.error('Error fetching transaction by ID:', error) - res.status(500).json({ error: 'Internal server error' }) - } -}) - -app.get('/api/refunded-and-relayed-transactions', async (req, res) => { - try { - const transactions = await resolvers.Query.refundedAndRelayedTransactions() - res.json(transactions) - } catch (error) { - console.error('Error fetching refunded and relayed transactions:', error) - res.status(500).json({ error: 'Internal server error' }) - } -}) - -app.listen(process.env.PORT, () => { - console.info('API server runs on http://localhost:3001') - console.info('REST requests go through http://localhost:3001/api') - console.info('GraphQL requests go through http://localhost:3001/graphql') -}) diff --git a/packages/rfq-indexer/api/src/jest.config.js b/packages/rfq-indexer/api/src/jest.config.js new file mode 100644 index 0000000000..ba447263ea --- /dev/null +++ b/packages/rfq-indexer/api/src/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', ''], +} diff --git a/packages/rfq-indexer/api/src/middleware/showFirstValidationError.ts b/packages/rfq-indexer/api/src/middleware/showFirstValidationError.ts new file mode 100644 index 0000000000..29ccda3e43 --- /dev/null +++ b/packages/rfq-indexer/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/rfq-indexer/api/src/queries/claimsQueries.ts b/packages/rfq-indexer/api/src/queries/claimsQueries.ts new file mode 100644 index 0000000000..8ce64a5e3e --- /dev/null +++ b/packages/rfq-indexer/api/src/queries/claimsQueries.ts @@ -0,0 +1,17 @@ +import { db } from '../db' + +// typical fields to return for a BridgeDepositClaimed event when it is joined to a BridgeRequest +export const qClaims = () => { + return db + .selectFrom('BridgeDepositClaimedEvents') + .select([ + 'BridgeDepositClaimedEvents.transactionId as transactionId_claim', + 'BridgeDepositClaimedEvents.blockNumber as blockNumber_claim', + 'BridgeDepositClaimedEvents.blockTimestamp as blockTimestamp_claim', + 'BridgeDepositClaimedEvents.transactionHash as transactionHash_claim', + + 'BridgeDepositClaimedEvents.to as to_claim', + 'BridgeDepositClaimedEvents.relayer as relayer_claim', + 'BridgeDepositClaimedEvents.amountFormatted as amountFormatted_claim', + ]) +} diff --git a/packages/rfq-indexer/api/src/queries/depositsQueries.ts b/packages/rfq-indexer/api/src/queries/depositsQueries.ts new file mode 100644 index 0000000000..61e33aa3d1 --- /dev/null +++ b/packages/rfq-indexer/api/src/queries/depositsQueries.ts @@ -0,0 +1,26 @@ +import { db } from '../db' + +export const qDeposits = () => { + return db + .selectFrom('BridgeRequestEvents') + .select([ + 'BridgeRequestEvents.transactionId as transactionId_deposit', + 'BridgeRequestEvents.blockNumber as blockNumber_deposit', + 'BridgeRequestEvents.blockTimestamp as blockTimestamp_deposit', + 'BridgeRequestEvents.transactionHash as transactionHash_deposit', + 'BridgeRequestEvents.originChain', + 'BridgeRequestEvents.destChain', + 'BridgeRequestEvents.originChainId', + 'BridgeRequestEvents.destChainId', + 'BridgeRequestEvents.originToken', + 'BridgeRequestEvents.destToken', + 'BridgeRequestEvents.originAmountFormatted', + 'BridgeRequestEvents.destAmountFormatted', + 'BridgeRequestEvents.sender', + 'BridgeRequestEvents.sendChainGas', + ]) + .where('BridgeRequestEvents.blockTimestamp', '>', 1722729600) + // if index is partially loaded, we must limit lookback or will have various data issues from relays + // that happened to be in flight at the point of the index's start. + // may also improve query performance +} diff --git a/packages/rfq-indexer/api/src/queries/index.ts b/packages/rfq-indexer/api/src/queries/index.ts new file mode 100644 index 0000000000..72bad2522e --- /dev/null +++ b/packages/rfq-indexer/api/src/queries/index.ts @@ -0,0 +1,5 @@ +export { qClaims } from './claimsQueries' +export { qDeposits } from './depositsQueries' +export { qProofs } from './proofsQueries' +export { qRefunds } from './refundsQueries' +export { qRelays } from './relaysQueries' diff --git a/packages/rfq-indexer/api/src/queries/proofsQueries.ts b/packages/rfq-indexer/api/src/queries/proofsQueries.ts new file mode 100644 index 0000000000..e7a1ccc012 --- /dev/null +++ b/packages/rfq-indexer/api/src/queries/proofsQueries.ts @@ -0,0 +1,15 @@ +import { db } from '../db' + +// typical fields to return for a BridgeProofProvided event when it is joined to a BridgeRequest +export const qProofs = () => { + return db + .selectFrom('BridgeProofProvidedEvents') + .select([ + 'BridgeProofProvidedEvents.transactionId as transactionId_proof', + 'BridgeProofProvidedEvents.blockNumber as blockNumber_proof', + 'BridgeProofProvidedEvents.blockTimestamp as blockTimestamp_proof', + 'BridgeProofProvidedEvents.transactionHash as transactionHash_proof', + + 'BridgeProofProvidedEvents.relayer as relayer_proof', + ]) +} diff --git a/packages/rfq-indexer/api/src/queries/refundsQueries.ts b/packages/rfq-indexer/api/src/queries/refundsQueries.ts new file mode 100644 index 0000000000..9de6f72773 --- /dev/null +++ b/packages/rfq-indexer/api/src/queries/refundsQueries.ts @@ -0,0 +1,15 @@ +import { db } from '../db' + +export const qRefunds = () => { + return db + .selectFrom('BridgeDepositRefundedEvents') + .select([ + 'BridgeDepositRefundedEvents.transactionId as transactionId_refund', + 'BridgeDepositRefundedEvents.blockNumber as blockNumber_refund', + 'BridgeDepositRefundedEvents.blockTimestamp as blockTimestamp_refund', + 'BridgeDepositRefundedEvents.transactionHash as transactionHash_refund', + + 'BridgeDepositRefundedEvents.to as to_refund', + 'BridgeDepositRefundedEvents.amountFormatted as amountFormatted_refund', + ]) +} diff --git a/packages/rfq-indexer/api/src/queries/relaysQueries.ts b/packages/rfq-indexer/api/src/queries/relaysQueries.ts new file mode 100644 index 0000000000..bd7b3990a0 --- /dev/null +++ b/packages/rfq-indexer/api/src/queries/relaysQueries.ts @@ -0,0 +1,16 @@ +import { db } from '../db' + +// typical fields to return for a BridgeRelayed event when it is joined to a BridgeRequest +export const qRelays = () => { + return db + .selectFrom('BridgeRelayedEvents') + .select([ + 'BridgeRelayedEvents.transactionId as transactionId_relay', + 'BridgeRelayedEvents.blockNumber as blockNumber_relay', + 'BridgeRelayedEvents.blockTimestamp as blockTimestamp_relay', + 'BridgeRelayedEvents.transactionHash as transactionHash_relay', + + 'BridgeRelayedEvents.relayer as relayer_relay', + 'BridgeRelayedEvents.to as to_relay', + ]) +} diff --git a/packages/rfq-indexer/api/src/routes/conflictingProofsRoute.ts b/packages/rfq-indexer/api/src/routes/conflictingProofsRoute.ts new file mode 100644 index 0000000000..aa48c89e07 --- /dev/null +++ b/packages/rfq-indexer/api/src/routes/conflictingProofsRoute.ts @@ -0,0 +1,65 @@ +import express from 'express' + +import { conflictingProofsController } from '../controllers/conflictingProofsController' + +const router = express.Router() + +/** + * @openapi + * /conflicting-proofs: + * get: + * summary: Get conflicting proofs + * description: Retrieves a list of transactions where the relayer in the proof differs from the relayer in the relay event + * responses: + * 200: + * description: Successful response + * content: + * application/json: + * schema: + * type: array + * items: + * type: object + * properties: + * Bridge: + * type: object + * description: General transaction fields + * BridgeRequest: + * type: object + * description: Deposit information + * BridgeRelay: + * type: object + * description: Relay information + * BridgeRefund: + * type: object + * description: Refund information + * BridgeProof: + * type: object + * description: Proof information (if available) + * BridgeClaim: + * type: object + * description: Claim information (if available) + * BridgeDispute: + * type: object + * description: Dispute information (if available) + * 404: + * description: No conflicting proofs found + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + */ +router.get('/', conflictingProofsController) + +export default router diff --git a/packages/rfq-indexer/api/src/routes/index.ts b/packages/rfq-indexer/api/src/routes/index.ts new file mode 100644 index 0000000000..302f878781 --- /dev/null +++ b/packages/rfq-indexer/api/src/routes/index.ts @@ -0,0 +1,17 @@ +import express from 'express' + +import pendingTransactionsRoute from './pendingTransactionsRoute' +import refundedAndRelayedRoute from './refundedAndRelayedRoute' +import invalidRelaysRoute from './invalidRelaysRoute' +import conflictingProofsRoute from './conflictingProofsRoute' +import transactionIdRoute from './transactionIdRoute' + +const router = express.Router() + +router.use('/pending-transactions', pendingTransactionsRoute) +router.use('/refunded-and-relayed', refundedAndRelayedRoute) +router.use('/invalid-relays', invalidRelaysRoute) +router.use('/conflicting-proofs', conflictingProofsRoute) +router.use('/transaction-id', transactionIdRoute) + +export default router diff --git a/packages/rfq-indexer/api/src/routes/invalidRelaysRoute.ts b/packages/rfq-indexer/api/src/routes/invalidRelaysRoute.ts new file mode 100644 index 0000000000..31356156f2 --- /dev/null +++ b/packages/rfq-indexer/api/src/routes/invalidRelaysRoute.ts @@ -0,0 +1,65 @@ +import express from 'express' + +import { recentInvalidRelaysController } from '../controllers/invalidRelaysController' + +const router = express.Router() + +/** + * @openapi + * /invalid-relays: + * get: + * summary: Get recent invalid relays + * description: Retrieves a list of recent invalid relay events from the past 2 weeks + * responses: + * 200: + * description: Successful response + * content: + * application/json: + * schema: + * type: array + * items: + * type: object + * properties: + * Bridge: + * type: object + * description: General transaction fields + * BridgeRequest: + * type: object + * description: Deposit information + * BridgeRelay: + * type: object + * description: Relay information + * BridgeRefund: + * type: object + * description: Refund information + * BridgeProof: + * type: object + * description: Proof information (if available) + * BridgeClaim: + * type: object + * description: Claim information (if available) + * BridgeDispute: + * type: object + * description: Dispute information (if available) + * 404: + * description: No recent invalid relays found + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + */ +router.get('/', recentInvalidRelaysController) + +export default router diff --git a/packages/rfq-indexer/api/src/routes/pendingTransactionsRoute.ts b/packages/rfq-indexer/api/src/routes/pendingTransactionsRoute.ts new file mode 100644 index 0000000000..2dbeafb121 --- /dev/null +++ b/packages/rfq-indexer/api/src/routes/pendingTransactionsRoute.ts @@ -0,0 +1,149 @@ +import express from 'express' + +import { + pendingTransactionsMissingClaimController, + pendingTransactionsMissingProofController, + pendingTransactionsMissingRelayController +} from '../controllers/pendingTransactionsController' + +const router = express.Router() + +/** + * @openapi + * /pending-transactions/missing-claim: + * get: + * summary: Get pending transactions missing claim + * description: Retrieves a list of transactions that have been deposited, relayed, and proven, but not yet claimed + * responses: + * 200: + * description: Successful response + * content: + * application/json: + * schema: + * type: array + * items: + * type: object + * properties: + * Bridge: + * type: object + * description: General transaction fields + * BridgeRequest: + * type: object + * description: Deposit information + * BridgeRelay: + * type: object + * description: Relay information + * BridgeRefund: + * type: object + * description: Refund information + * BridgeProof: + * type: object + * description: Proof information (if available) + * BridgeClaim: + * type: object + * description: Claim information (if available) + * BridgeDispute: + * type: object + * description: Dispute information (if available) + * 404: + * description: No pending transactions missing claim found + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + */ +router.get('/missing-claim', pendingTransactionsMissingClaimController) + +/** + * @openapi + * /pending-transactions/missing-proof: + * get: + * summary: Get pending transactions missing proof + * description: Retrieves a list of transactions that have been deposited and relayed, but not yet proven + * responses: + * 200: + * description: Successful response + * content: + * application/json: + * schema: + * type: array + * items: + * type: object + * properties: + * deposit: + * type: object + * relay: + * type: object + * 404: + * description: No pending transactions missing proof found + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + */ +router.get('/missing-proof', pendingTransactionsMissingProofController) + +/** + * @openapi + * /pending-transactions/missing-relay: + * get: + * summary: Get pending transactions missing relay + * description: Retrieves a list of transactions that have been deposited, but not yet relayed or refunded + * responses: + * 200: + * description: Successful response + * content: + * application/json: + * schema: + * type: array + * items: + * type: object + * properties: + * deposit: + * type: object + * 404: + * description: No pending transactions missing relay found + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + */ +router.get('/missing-relay', pendingTransactionsMissingRelayController) + +export default router diff --git a/packages/rfq-indexer/api/src/routes/refundedAndRelayedRoute.ts b/packages/rfq-indexer/api/src/routes/refundedAndRelayedRoute.ts new file mode 100644 index 0000000000..cd38b3b33e --- /dev/null +++ b/packages/rfq-indexer/api/src/routes/refundedAndRelayedRoute.ts @@ -0,0 +1,65 @@ +import express from 'express' + +import { refundedAndRelayedTransactionsController } from '../controllers/refundedAndRelayedController' + +const router = express.Router() + +/** + * @openapi + * /refunded-and-relayed: + * get: + * summary: Get refunded and relayed transactions + * description: Retrieves a list of transactions that have been both refunded and relayed + * responses: + * 200: + * description: Successful response + * content: + * application/json: + * schema: + * type: array + * items: + * type: object + * properties: + * Bridge: + * type: object + * description: General transaction fields + * BridgeRequest: + * type: object + * description: Deposit information + * BridgeRelay: + * type: object + * description: Relay information + * BridgeRefund: + * type: object + * description: Refund information + * BridgeProof: + * type: object + * description: Proof information (if available) + * BridgeClaim: + * type: object + * description: Claim information (if available) + * BridgeDispute: + * type: object + * description: Dispute information (if available) + * 404: + * description: No refunded and relayed transactions found + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + */ +router.get('/', refundedAndRelayedTransactionsController) + +export default router diff --git a/packages/rfq-indexer/api/src/routes/transactionIdRoute.ts b/packages/rfq-indexer/api/src/routes/transactionIdRoute.ts new file mode 100644 index 0000000000..ef0b4077a4 --- /dev/null +++ b/packages/rfq-indexer/api/src/routes/transactionIdRoute.ts @@ -0,0 +1,70 @@ +import express from 'express' + +import { getTransactionById } from '../controllers/transactionIdController' + +const router = express.Router() + +/** + * @openapi + * /transaction-id/{transactionId}: + * get: + * summary: Get transaction details by ID + * description: Retrieves detailed information about a transaction, including deposit, relay, proof, claim, and refund data if available + * parameters: + * - in: path + * name: transactionId + * required: true + * schema: + * type: string + * description: The unique identifier of the transaction + * responses: + * 200: + * description: Successful response + * content: + * application/json: + * schema: + * type: object + * properties: +* Bridge: +* type: object +* description: General transaction fields +* BridgeRequest: +* type: object +* description: Deposit information +* BridgeRelay: +* type: object +* description: Relay information +* BridgeRefund: +* type: object +* description: Refund information +* BridgeProof: +* type: object +* description: Proof information (if available) +* BridgeClaim: +* type: object +* description: Claim information (if available) +* BridgeDispute: +* type: object +* description: Dispute information (if available) + * 404: + * description: Transaction not found + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * 500: + * description: Server error + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + */ +router.get('/:transactionId', getTransactionById) + +export default router diff --git a/packages/rfq-indexer/api/src/swagger.ts b/packages/rfq-indexer/api/src/swagger.ts new file mode 100644 index 0000000000..aec6d0fb43 --- /dev/null +++ b/packages/rfq-indexer/api/src/swagger.ts @@ -0,0 +1,28 @@ +import swaggerJsdoc from 'swagger-jsdoc' + +const isDevelopment = process.env.NODE_ENV === 'development' + +const devServer = { + url: 'http://localhost:3001/api', + description: 'Local Development Server', +} + +const prodServer = { + url: 'https://triumphant-magic-production.up.railway.app/api', + description: 'Production Server', +} + +const options: swaggerJsdoc.Options = { + definition: { + openapi: '3.0.0', + info: { + title: 'RFQ Indexer API', + version: '1.0.00', + description: 'API documentation for the RFQ Indexer API', + }, + servers: isDevelopment ? [devServer, prodServer] : [prodServer, devServer], + }, + apis: ['./src/routes/*.ts', './src/*.ts'], +} + +export const specs = swaggerJsdoc(options) diff --git a/packages/synapse-constants/unsupportedTokens.txt b/packages/rfq-indexer/api/src/utils/isTransaction.ts similarity index 100% rename from packages/synapse-constants/unsupportedTokens.txt rename to packages/rfq-indexer/api/src/utils/isTransaction.ts diff --git a/packages/rfq-indexer/api/src/utils/nestResults.ts b/packages/rfq-indexer/api/src/utils/nestResults.ts new file mode 100644 index 0000000000..6d31050362 --- /dev/null +++ b/packages/rfq-indexer/api/src/utils/nestResults.ts @@ -0,0 +1,58 @@ +export const nest_results = (sqlResults: any[]) => { + return sqlResults.map((transaction: any) => { + const bridgeRequest: { [key: string]: any } = {} + const bridgeRelay: { [key: string]: any } = {} + const bridgeProof: { [key: string]: any } = {} + const bridgeClaim: { [key: string]: any } = {} + const bridgeRefund: { [key: string]: any } = {} + const bridgeDispute: { [key: string]: any } = {} + const transactionFields: { [key: string]: any } = {} + + let transactionIdSet = false + + for (const [key, value] of Object.entries(transaction)) { + if (key.startsWith('transactionId')) { + if (!transactionIdSet) { + transactionFields[key.replace(/_.+$/, '')] = value + transactionIdSet = true + } + // Ignore other transactionId fields + } else if (key.endsWith('_deposit')) { + bridgeRequest[key.replace('_deposit', '')] = value + } else if (key.endsWith('_relay')) { + bridgeRelay[key.replace('_relay', '')] = value + } else if (key.endsWith('_proof')) { + bridgeProof[key.replace('_proof', '')] = value + } else if (key.endsWith('_claim')) { + bridgeClaim[key.replace('_claim', '')] = value + } else if (key.endsWith('_refund')) { + bridgeRefund[key.replace('_refund', '')] = value + } else if (key.endsWith('_dispute')) { + bridgeDispute[key.replace('_dispute', '')] = value + } else { + transactionFields[key] = value + } + } + + const result: { [key: string]: any } = { Bridge: transactionFields } + if (Object.keys(bridgeRequest).length) { + result.BridgeRequest = bridgeRequest + } + if (Object.keys(bridgeRelay).length) { + result.BridgeRelay = bridgeRelay + } + if (Object.keys(bridgeProof).length) { + result.BridgeProof = bridgeProof + } + if (Object.keys(bridgeClaim).length) { + result.BridgeClaim = bridgeClaim + } + if (Object.keys(bridgeRefund).length) { + result.BridgeRefund = bridgeRefund + } + if (Object.keys(bridgeDispute).length) { + result.BridgeDispute = bridgeDispute + } + return result + }) +} diff --git a/packages/sdk-router/CHANGELOG.md b/packages/sdk-router/CHANGELOG.md index f66f3b5ab4..fff8579edf 100644 --- a/packages/sdk-router/CHANGELOG.md +++ b/packages/sdk-router/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.11.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/sdk-router@0.11.1...@synapsecns/sdk-router@0.11.2) (2024-09-26) + + +### Bug Fixes + +* **sdk-router:** disable ARB airdrop tests ([#3195](https://github.com/synapsecns/sanguine/issues/3195)) ([fc6ddae](https://github.com/synapsecns/sanguine/commit/fc6ddaedf03f7769dab362f0bcdf81a3dd010516)) + + + + + ## [0.11.1](https://github.com/synapsecns/sanguine/compare/@synapsecns/sdk-router@0.11.0...@synapsecns/sdk-router@0.11.1) (2024-09-04) **Note:** Version bump only for package @synapsecns/sdk-router diff --git a/packages/sdk-router/package.json b/packages/sdk-router/package.json index 56f4c91bea..6042ce0125 100644 --- a/packages/sdk-router/package.json +++ b/packages/sdk-router/package.json @@ -1,7 +1,7 @@ { "name": "@synapsecns/sdk-router", "description": "An SDK for interacting with the Synapse Protocol", - "version": "0.11.1", + "version": "0.11.2", "license": "MIT", "main": "dist/index.js", "typings": "dist/index.d.ts", diff --git a/packages/sdk-router/src/sdk.test.ts b/packages/sdk-router/src/sdk.test.ts index 4d49d9da45..fd79df50d2 100644 --- a/packages/sdk-router/src/sdk.test.ts +++ b/packages/sdk-router/src/sdk.test.ts @@ -54,7 +54,8 @@ global.fetch = jest.fn(() => const EXPECTED_GAS_DROP: { [chainId: number]: BigNumber } = { [SupportedChainId.ETH]: BigNumber.from(0), - [SupportedChainId.ARBITRUM]: parseFixed('0.0003', 18), + // TODO: reenable once both ARB airdrops are adjusted + // [SupportedChainId.ARBITRUM]: parseFixed('0.0003', 18), [SupportedChainId.BSC]: parseFixed('0.002', 18), [SupportedChainId.AVALANCHE]: parseFixed('0.025', 18), } @@ -296,9 +297,10 @@ describe('SynapseSDK', () => { MEDIAN_TIME_BRIDGE[SupportedChainId.ETH] ) expect(result.bridgeModuleName).toEqual('SynapseBridge') - expect(result.gasDropAmount).toEqual( - EXPECTED_GAS_DROP[SupportedChainId.ARBITRUM] - ) + // TODO: reenable + // expect(result.gasDropAmount).toEqual( + // EXPECTED_GAS_DROP[SupportedChainId.ARBITRUM] + // ) expect(result.originChainId).toEqual(SupportedChainId.ETH) expect(result.destChainId).toEqual(SupportedChainId.ARBITRUM) }) @@ -814,12 +816,13 @@ describe('SynapseSDK', () => { allQuotes[0].bridgeModuleName === 'SynapseCCTP' || allQuotes[1].bridgeModuleName === 'SynapseCCTP' ).toBe(true) - expect(allQuotes[0].gasDropAmount).toEqual( - EXPECTED_GAS_DROP[SupportedChainId.ARBITRUM] - ) - expect(allQuotes[1].gasDropAmount).toEqual( - EXPECTED_GAS_DROP[SupportedChainId.ARBITRUM] - ) + // TODO: reenable + // expect(allQuotes[0].gasDropAmount).toEqual( + // EXPECTED_GAS_DROP[SupportedChainId.ARBITRUM] + // ) + // expect(allQuotes[1].gasDropAmount).toEqual( + // EXPECTED_GAS_DROP[SupportedChainId.ARBITRUM] + // ) expect(allQuotes[0].originChainId).toEqual(SupportedChainId.ETH) expect(allQuotes[0].destChainId).toEqual(SupportedChainId.ARBITRUM) expect(allQuotes[1].originChainId).toEqual(SupportedChainId.ETH) @@ -848,9 +851,10 @@ describe('SynapseSDK', () => { expect(allQuotes.length).toEqual(1) expectCorrectBridgeQuote(allQuotes[0]) expect(allQuotes[0].bridgeModuleName).toEqual('SynapseBridge') - expect(allQuotes[0].gasDropAmount).toEqual( - EXPECTED_GAS_DROP[SupportedChainId.ARBITRUM] - ) + // TODO: reenable + // expect(allQuotes[0].gasDropAmount).toEqual( + // EXPECTED_GAS_DROP[SupportedChainId.ARBITRUM] + // ) }) }) diff --git a/packages/solidity-devops/.vscode/settings.json b/packages/solidity-devops/.vscode/settings.json new file mode 100644 index 0000000000..9cab755557 --- /dev/null +++ b/packages/solidity-devops/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "[solidity]": { + "editor.defaultFormatter": "JuanBlanco.solidity" + }, + "solidity.formatter": "forge", + "solidity.monoRepoSupport": false +} diff --git a/packages/solidity-devops/CHANGELOG.md b/packages/solidity-devops/CHANGELOG.md index 38e5edb4bf..262bd0a971 100644 --- a/packages/solidity-devops/CHANGELOG.md +++ b/packages/solidity-devops/CHANGELOG.md @@ -3,6 +3,25 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.4.6](https://github.com/synapsecns/sanguine/compare/@synapsecns/solidity-devops@0.4.5...@synapsecns/solidity-devops@0.4.6) (2024-09-24) + + +### Bug Fixes + +* **contracts-rfq:** CI workflows [SLT-245] ([#3178](https://github.com/synapsecns/sanguine/issues/3178)) ([74b620e](https://github.com/synapsecns/sanguine/commit/74b620e4c928be8d0dbb422708376d167db7848d)) + + + + + +## [0.4.5](https://github.com/synapsecns/sanguine/compare/@synapsecns/solidity-devops@0.4.4...@synapsecns/solidity-devops@0.4.5) (2024-09-23) + +**Note:** Version bump only for package @synapsecns/solidity-devops + + + + + ## [0.4.4](https://github.com/synapsecns/sanguine/compare/@synapsecns/solidity-devops@0.4.3...@synapsecns/solidity-devops@0.4.4) (2024-07-17) diff --git a/packages/solidity-devops/package.json b/packages/solidity-devops/package.json index 713845fae2..b69ffa6e81 100644 --- a/packages/solidity-devops/package.json +++ b/packages/solidity-devops/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/solidity-devops", - "version": "0.4.4", + "version": "0.4.6", "description": "A collection of utils to effortlessly test, deploy and maintain the smart contracts on EVM compatible blockchains", "license": "MIT", "repository": { @@ -40,6 +40,6 @@ "vp": "js/verifyProxy.js" }, "devDependencies": { - "solhint": "^4.5.4" + "solhint": "5.0.3" } } diff --git a/packages/synapse-constants/.eslintrc.cjs b/packages/synapse-constants/.eslintrc.cjs new file mode 100644 index 0000000000..32e72b00e0 --- /dev/null +++ b/packages/synapse-constants/.eslintrc.cjs @@ -0,0 +1,12 @@ +module.exports = { + extends: '../../.eslintrc.js', + overrides: [ + { + files: ['**/*.ts'], + rules: { + 'guard-for-in': 'off', + 'prefer-arrow/prefer-arrow-functions': 'off', + }, + }, + ], +} diff --git a/packages/synapse-constants/CHANGELOG.md b/packages/synapse-constants/CHANGELOG.md index a229762afc..ea13cbea97 100644 --- a/packages/synapse-constants/CHANGELOG.md +++ b/packages/synapse-constants/CHANGELOG.md @@ -3,6 +3,41 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.5.6](https://github.com/synapsecns/sanguine/compare/synapse-constants@1.5.4...synapse-constants@1.5.6) (2024-10-01) + +**Note:** Version bump only for package synapse-constants + + + + + +## [1.5.4](https://github.com/synapsecns/sanguine/compare/synapse-constants@1.5.2...synapse-constants@1.5.4) (2024-10-01) + +**Note:** Version bump only for package synapse-constants + + + + + +## [1.5.2](https://github.com/synapsecns/sanguine/compare/synapse-constants@1.5.0...synapse-constants@1.5.2) (2024-10-01) + +**Note:** Version bump only for package synapse-constants + + + + + +# [1.5.0](https://github.com/synapsecns/sanguine/compare/synapse-constants@1.3.24...synapse-constants@1.5.0) (2024-10-01) + + +### Features + +* **synapse-constants:** Refactor with rollup for package build and export [SLT-160] ([#3175](https://github.com/synapsecns/sanguine/issues/3175)) ([32cee8e](https://github.com/synapsecns/sanguine/commit/32cee8e3bb88222e9876b0963effb51d72be31a6)) + + + + + ## [1.3.24](https://github.com/synapsecns/sanguine/compare/synapse-constants@1.3.23...synapse-constants@1.3.24) (2024-09-10) **Note:** Version bump only for package synapse-constants diff --git a/packages/synapse-constants/constants/assets/chains/arbitrum.svg b/packages/synapse-constants/constants/assets/chains/arbitrum.svg deleted file mode 100644 index cb285a937a..0000000000 --- a/packages/synapse-constants/constants/assets/chains/arbitrum.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/aurora.svg b/packages/synapse-constants/constants/assets/chains/aurora.svg deleted file mode 100644 index 5b5441b3dd..0000000000 --- a/packages/synapse-constants/constants/assets/chains/aurora.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/avalanche.svg b/packages/synapse-constants/constants/assets/chains/avalanche.svg deleted file mode 100644 index 5695785b66..0000000000 --- a/packages/synapse-constants/constants/assets/chains/avalanche.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/base.svg b/packages/synapse-constants/constants/assets/chains/base.svg deleted file mode 100644 index 5dd1e67af0..0000000000 --- a/packages/synapse-constants/constants/assets/chains/base.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/blast.svg b/packages/synapse-constants/constants/assets/chains/blast.svg deleted file mode 100644 index 5df8a7f017..0000000000 --- a/packages/synapse-constants/constants/assets/chains/blast.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/synapse-constants/constants/assets/chains/bnb.svg b/packages/synapse-constants/constants/assets/chains/bnb.svg deleted file mode 100644 index 4279646a3d..0000000000 --- a/packages/synapse-constants/constants/assets/chains/bnb.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/boba.svg b/packages/synapse-constants/constants/assets/chains/boba.svg deleted file mode 100644 index 6f3f19fdfa..0000000000 --- a/packages/synapse-constants/constants/assets/chains/boba.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/canto.svg b/packages/synapse-constants/constants/assets/chains/canto.svg deleted file mode 100644 index 20e0fe2177..0000000000 --- a/packages/synapse-constants/constants/assets/chains/canto.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/cronos.svg b/packages/synapse-constants/constants/assets/chains/cronos.svg deleted file mode 100644 index f2c429fc7f..0000000000 --- a/packages/synapse-constants/constants/assets/chains/cronos.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/dfk.svg b/packages/synapse-constants/constants/assets/chains/dfk.svg deleted file mode 100644 index 7daf4cc71e..0000000000 --- a/packages/synapse-constants/constants/assets/chains/dfk.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/dogechain.svg b/packages/synapse-constants/constants/assets/chains/dogechain.svg deleted file mode 100644 index 9c8a52c0cb..0000000000 --- a/packages/synapse-constants/constants/assets/chains/dogechain.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/ethereum.svg b/packages/synapse-constants/constants/assets/chains/ethereum.svg deleted file mode 100644 index 407dcd3072..0000000000 --- a/packages/synapse-constants/constants/assets/chains/ethereum.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/fantom.svg b/packages/synapse-constants/constants/assets/chains/fantom.svg deleted file mode 100644 index 3e23ff5c0e..0000000000 --- a/packages/synapse-constants/constants/assets/chains/fantom.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/harmony.svg b/packages/synapse-constants/constants/assets/chains/harmony.svg deleted file mode 100644 index 4ce9dc2a07..0000000000 --- a/packages/synapse-constants/constants/assets/chains/harmony.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/index.ts b/packages/synapse-constants/constants/assets/chains/index.ts deleted file mode 100644 index ea0e9e1205..0000000000 --- a/packages/synapse-constants/constants/assets/chains/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -export * as arbitrumImg from './arbitrum.svg' -export * as auroraImg from './aurora.svg' -export * as avalancheImg from './avalanche.svg' -export * as baseImg from './base.svg' -export * as bobaImg from './boba.svg' -export * as bscImg from './bnb.svg' -export * as cantoImg from './canto.svg' -export * as cronosImg from './cronos.svg' -export * as dfkImg from './dfk.svg' -export * as dogechainImg from './dogechain.svg' -export * as ethImg from './ethereum.svg' -export * as fantomImg from './fantom.svg' -export * as harmonyImg from './harmony.svg' -export * as klaytnImg from './klaytn.svg' -export * as metisImg from './metis.svg' -export * as moonbeamImg from './moonbeam.svg' -export * as moonriverImg from './moonriver.svg' -export * as optimismImg from './optimism.svg' -export * as polygonImg from './polygon.svg' -export * as lineaImg from './linea.svg' diff --git a/packages/synapse-constants/constants/assets/chains/klaytn.svg b/packages/synapse-constants/constants/assets/chains/klaytn.svg deleted file mode 100644 index 88efdb79b5..0000000000 --- a/packages/synapse-constants/constants/assets/chains/klaytn.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/linea.svg b/packages/synapse-constants/constants/assets/chains/linea.svg deleted file mode 100644 index 8639b02212..0000000000 --- a/packages/synapse-constants/constants/assets/chains/linea.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/synapse-constants/constants/assets/chains/metis.svg b/packages/synapse-constants/constants/assets/chains/metis.svg deleted file mode 100644 index fd1342a2c0..0000000000 --- a/packages/synapse-constants/constants/assets/chains/metis.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/moonbeam.svg b/packages/synapse-constants/constants/assets/chains/moonbeam.svg deleted file mode 100644 index 0cbeda36a7..0000000000 --- a/packages/synapse-constants/constants/assets/chains/moonbeam.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/moonriver.svg b/packages/synapse-constants/constants/assets/chains/moonriver.svg deleted file mode 100644 index 07cf4ddfcd..0000000000 --- a/packages/synapse-constants/constants/assets/chains/moonriver.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/optimism.svg b/packages/synapse-constants/constants/assets/chains/optimism.svg deleted file mode 100644 index 7378f33b2b..0000000000 --- a/packages/synapse-constants/constants/assets/chains/optimism.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/polygon.svg b/packages/synapse-constants/constants/assets/chains/polygon.svg deleted file mode 100644 index 879ae5a82c..0000000000 --- a/packages/synapse-constants/constants/assets/chains/polygon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/chains/scroll.svg b/packages/synapse-constants/constants/assets/chains/scroll.svg deleted file mode 100644 index fbfa194347..0000000000 --- a/packages/synapse-constants/constants/assets/chains/scroll.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/packages/synapse-constants/constants/assets/explorer/arbitrum.svg b/packages/synapse-constants/constants/assets/explorer/arbitrum.svg deleted file mode 100644 index 24c3ecc168..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/arbitrum.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/aurora.svg b/packages/synapse-constants/constants/assets/explorer/aurora.svg deleted file mode 100644 index 4a8683c6ee..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/aurora.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/avalanche.svg b/packages/synapse-constants/constants/assets/explorer/avalanche.svg deleted file mode 100644 index 7b3a778c54..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/avalanche.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/basescan.svg b/packages/synapse-constants/constants/assets/explorer/basescan.svg deleted file mode 100644 index fdefbf2fee..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/basescan.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/blast.svg b/packages/synapse-constants/constants/assets/explorer/blast.svg deleted file mode 100644 index 5df8a7f017..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/blast.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/synapse-constants/constants/assets/explorer/boba.svg b/packages/synapse-constants/constants/assets/explorer/boba.svg deleted file mode 100644 index a39fb90aac..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/boba.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/bscscan.svg b/packages/synapse-constants/constants/assets/explorer/bscscan.svg deleted file mode 100644 index a96d66873d..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/bscscan.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/canto.svg b/packages/synapse-constants/constants/assets/explorer/canto.svg deleted file mode 100644 index 4032a81b40..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/canto.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/cronos.svg b/packages/synapse-constants/constants/assets/explorer/cronos.svg deleted file mode 100644 index d35b790126..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/cronos.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/dfk-chain.svg b/packages/synapse-constants/constants/assets/explorer/dfk-chain.svg deleted file mode 100644 index ea73403433..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/dfk-chain.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/dogechain.svg b/packages/synapse-constants/constants/assets/explorer/dogechain.svg deleted file mode 100644 index 9c8a52c0cb..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/dogechain.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/etherscan.svg b/packages/synapse-constants/constants/assets/explorer/etherscan.svg deleted file mode 100644 index a826445d2d..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/etherscan.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/fantom.svg b/packages/synapse-constants/constants/assets/explorer/fantom.svg deleted file mode 100644 index 7984eaf5ac..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/fantom.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/harmony.svg b/packages/synapse-constants/constants/assets/explorer/harmony.svg deleted file mode 100644 index 6b75928be0..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/harmony.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/index.ts b/packages/synapse-constants/constants/assets/explorer/index.ts deleted file mode 100644 index c826a27ea9..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -export * as ethExplorerImg from './etherscan.svg' -export * as arbitrumExplorerImg from './arbitrum.svg' -export * as bnbExplorerImg from './bscscan.svg' -export * as avalancheExplorerImg from './avalanche.svg' -export * as cantoExplorerImg from './canto.svg' -export * as optimismExplorerImg from './optimism.svg' -export * as polygonExplorerImg from './polygon.svg' -export * as dfkExplorerImg from './dfk-chain.svg' -export * as klaytynExplorerImg from './klaytn.svg' -export * as fantomExplorerImg from './fantom.svg' -export * as cronosExplorerImg from './cronos.svg' -export * as bobaExplorerImg from './boba.svg' -export * as metisExplorerImg from './metis.svg' -export * as auroraExplorerImg from './aurora.svg' -export * as harmonyExplorerImg from './harmony.svg' -export * as moonbeamExplorerImg from './moonbeam.svg' -export * as moonriverExplorerImg from './moonriver.svg' -export * as dogeExplorerImg from './dogechain.svg' -export * as baseExplorerImg from './basescan.svg' -export * as scrollExplorerImg from './scroll.svg' -export * as lineaExplorerImg from './linea.svg' diff --git a/packages/synapse-constants/constants/assets/explorer/klaytn.svg b/packages/synapse-constants/constants/assets/explorer/klaytn.svg deleted file mode 100644 index cb07c44c78..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/klaytn.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/linea.svg b/packages/synapse-constants/constants/assets/explorer/linea.svg deleted file mode 100644 index 8639b02212..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/linea.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/synapse-constants/constants/assets/explorer/metis.svg b/packages/synapse-constants/constants/assets/explorer/metis.svg deleted file mode 100644 index fd1342a2c0..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/metis.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/moonbeam.svg b/packages/synapse-constants/constants/assets/explorer/moonbeam.svg deleted file mode 100644 index cc65830b75..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/moonbeam.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/moonriver.svg b/packages/synapse-constants/constants/assets/explorer/moonriver.svg deleted file mode 100644 index 94984f7ba0..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/moonriver.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/optimism.svg b/packages/synapse-constants/constants/assets/explorer/optimism.svg deleted file mode 100644 index 6650dfa669..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/optimism.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/polygon.svg b/packages/synapse-constants/constants/assets/explorer/polygon.svg deleted file mode 100644 index a3a486eb43..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/polygon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/explorer/scroll.svg b/packages/synapse-constants/constants/assets/explorer/scroll.svg deleted file mode 100644 index fbfa194347..0000000000 --- a/packages/synapse-constants/constants/assets/explorer/scroll.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/packages/synapse-constants/constants/assets/icons/ageur.svg b/packages/synapse-constants/constants/assets/icons/ageur.svg deleted file mode 100644 index e48ff825bc..0000000000 --- a/packages/synapse-constants/constants/assets/icons/ageur.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/avax.svg b/packages/synapse-constants/constants/assets/icons/avax.svg deleted file mode 100644 index 5695785b66..0000000000 --- a/packages/synapse-constants/constants/assets/icons/avax.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/avweth.svg b/packages/synapse-constants/constants/assets/icons/avweth.svg deleted file mode 100644 index 8e9d055972..0000000000 --- a/packages/synapse-constants/constants/assets/icons/avweth.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/btc.svg b/packages/synapse-constants/constants/assets/icons/btc.svg deleted file mode 100644 index a10e2bcc2c..0000000000 --- a/packages/synapse-constants/constants/assets/icons/btc.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/busd.svg b/packages/synapse-constants/constants/assets/icons/busd.svg deleted file mode 100644 index 939021a69d..0000000000 --- a/packages/synapse-constants/constants/assets/icons/busd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/crvusd.svg b/packages/synapse-constants/constants/assets/icons/crvusd.svg deleted file mode 100644 index de40a549fc..0000000000 --- a/packages/synapse-constants/constants/assets/icons/crvusd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/dai.svg b/packages/synapse-constants/constants/assets/icons/dai.svg deleted file mode 100644 index 41d6d21513..0000000000 --- a/packages/synapse-constants/constants/assets/icons/dai.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/dog.svg b/packages/synapse-constants/constants/assets/icons/dog.svg deleted file mode 100644 index 1d7640718a..0000000000 --- a/packages/synapse-constants/constants/assets/icons/dog.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/eth.svg b/packages/synapse-constants/constants/assets/icons/eth.svg deleted file mode 100644 index e9dcc7f0c3..0000000000 --- a/packages/synapse-constants/constants/assets/icons/eth.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/frax.svg b/packages/synapse-constants/constants/assets/icons/frax.svg deleted file mode 100644 index cf8c89f20d..0000000000 --- a/packages/synapse-constants/constants/assets/icons/frax.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/ftm.svg b/packages/synapse-constants/constants/assets/icons/ftm.svg deleted file mode 100644 index 81b17f14c6..0000000000 --- a/packages/synapse-constants/constants/assets/icons/ftm.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/fusdt.svg b/packages/synapse-constants/constants/assets/icons/fusdt.svg deleted file mode 100644 index 5d813d5824..0000000000 --- a/packages/synapse-constants/constants/assets/icons/fusdt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/gmx.svg b/packages/synapse-constants/constants/assets/icons/gmx.svg deleted file mode 100644 index c1534ad171..0000000000 --- a/packages/synapse-constants/constants/assets/icons/gmx.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/gohm.svg b/packages/synapse-constants/constants/assets/icons/gohm.svg deleted file mode 100644 index 7e4842d855..0000000000 --- a/packages/synapse-constants/constants/assets/icons/gohm.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/h2o.svg b/packages/synapse-constants/constants/assets/icons/h2o.svg deleted file mode 100644 index a2935082de..0000000000 --- a/packages/synapse-constants/constants/assets/icons/h2o.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/highstreet.svg b/packages/synapse-constants/constants/assets/icons/highstreet.svg deleted file mode 100644 index eb53b0ea3e..0000000000 --- a/packages/synapse-constants/constants/assets/icons/highstreet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/hyperjump.svg b/packages/synapse-constants/constants/assets/icons/hyperjump.svg deleted file mode 100644 index 5f8ca538f5..0000000000 --- a/packages/synapse-constants/constants/assets/icons/hyperjump.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/index.ts b/packages/synapse-constants/constants/assets/icons/index.ts deleted file mode 100644 index 540b13d226..0000000000 --- a/packages/synapse-constants/constants/assets/icons/index.ts +++ /dev/null @@ -1,44 +0,0 @@ -export * as ageurLogo from './ageur.svg' -export * as avaxLogo from './avax.svg' -export * as btcLogo from './btc.svg' -export * as busdLogo from './busd.svg' -export * as crvusdLogo from './crvusd.svg' -export * as linkLogo from './link.svg' -export * as daiLogo from './dai.svg' -export * as dogLogo from './dog.svg' -export * as ethLogo from './eth.svg' -export * as fraxLogo from './frax.svg' -export * as ftmLogo from './ftm.svg' -export * as gmxLogo from './gmx.svg' -export * as h2oLogo from './h2o.svg' -export * as highLogo from './highstreet.svg' -export * as hyperjumpLogo from './hyperjump.svg' -export * as jewelLogo from './jewel.png' -export * as klayLogo from './klay.svg' -export * as l2daoLogo from './l2dao.svg' -export * as maticLogo from './matic.svg' -export * as movrLogo from './movr.svg' -export * as nethLogo from './neth.svg' -export * as newoLogo from './newo.svg' -export * as nfdLogo from './nfd.svg' -export * as noteLogo from './note.svg' -export * as nusdLogo from './nusd.svg' -export * as ohmLogo from './ohm.svg' -export * as pepeLogo from './pepe.svg' -export * as plsLogo from './pls.svg' -export * as sdtLogo from './sdt.svg' -export * as sfiLogo from './sfi.svg' -export * as solarbeamLogo from './solar.svg' -export * as susdLogo from './susd.svg' -export * as synapseLogo from './syn.svg' -export * as unidexLogo from './unidex.svg' -export * as vstaLogo from './vsta.svg' -export * as wbtcLogo from './wbtc.svg' -export * as wethLogo from './weth.svg' -export * as avwethLogo from './avweth.svg' -export * as mimLogo from './mim.svg' -export * as usdcLogo from './usdc.svg' -export * as usdtLogo from './usdt.svg' -export * as usdbLogo from './usdc.svg' -export * as fusdtLogo from './usdt.svg' -export * as sushiLogo from './sushi.svg' diff --git a/packages/synapse-constants/constants/assets/icons/jewel.png b/packages/synapse-constants/constants/assets/icons/jewel.png deleted file mode 100644 index 999418cf80..0000000000 Binary files a/packages/synapse-constants/constants/assets/icons/jewel.png and /dev/null differ diff --git a/packages/synapse-constants/constants/assets/icons/klay.svg b/packages/synapse-constants/constants/assets/icons/klay.svg deleted file mode 100644 index 552e0a6e50..0000000000 --- a/packages/synapse-constants/constants/assets/icons/klay.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/synapse-constants/constants/assets/icons/l2dao.svg b/packages/synapse-constants/constants/assets/icons/l2dao.svg deleted file mode 100644 index 22175d2cb7..0000000000 --- a/packages/synapse-constants/constants/assets/icons/l2dao.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/link.svg b/packages/synapse-constants/constants/assets/icons/link.svg deleted file mode 100644 index cc848c331c..0000000000 --- a/packages/synapse-constants/constants/assets/icons/link.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/matic.svg b/packages/synapse-constants/constants/assets/icons/matic.svg deleted file mode 100644 index 879ae5a82c..0000000000 --- a/packages/synapse-constants/constants/assets/icons/matic.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/metamask.svg b/packages/synapse-constants/constants/assets/icons/metamask.svg deleted file mode 100644 index fa6d5b322b..0000000000 --- a/packages/synapse-constants/constants/assets/icons/metamask.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/mim.svg b/packages/synapse-constants/constants/assets/icons/mim.svg deleted file mode 100644 index 6397e92bae..0000000000 --- a/packages/synapse-constants/constants/assets/icons/mim.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/movr.svg b/packages/synapse-constants/constants/assets/icons/movr.svg deleted file mode 100644 index 07cf4ddfcd..0000000000 --- a/packages/synapse-constants/constants/assets/icons/movr.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/neth.svg b/packages/synapse-constants/constants/assets/icons/neth.svg deleted file mode 100644 index 913868f284..0000000000 --- a/packages/synapse-constants/constants/assets/icons/neth.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/newo.svg b/packages/synapse-constants/constants/assets/icons/newo.svg deleted file mode 100644 index 444ed2965f..0000000000 --- a/packages/synapse-constants/constants/assets/icons/newo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/nfd.svg b/packages/synapse-constants/constants/assets/icons/nfd.svg deleted file mode 100644 index 7534a91235..0000000000 --- a/packages/synapse-constants/constants/assets/icons/nfd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/note.svg b/packages/synapse-constants/constants/assets/icons/note.svg deleted file mode 100644 index c1be5c5dc9..0000000000 --- a/packages/synapse-constants/constants/assets/icons/note.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/nusd.svg b/packages/synapse-constants/constants/assets/icons/nusd.svg deleted file mode 100644 index 191a497c9e..0000000000 --- a/packages/synapse-constants/constants/assets/icons/nusd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/ohm.svg b/packages/synapse-constants/constants/assets/icons/ohm.svg deleted file mode 100644 index decefe65b8..0000000000 --- a/packages/synapse-constants/constants/assets/icons/ohm.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/op.svg b/packages/synapse-constants/constants/assets/icons/op.svg deleted file mode 100644 index bb5f2a1511..0000000000 --- a/packages/synapse-constants/constants/assets/icons/op.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/pepe.svg b/packages/synapse-constants/constants/assets/icons/pepe.svg deleted file mode 100644 index f08be7d1d6..0000000000 --- a/packages/synapse-constants/constants/assets/icons/pepe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/pls.svg b/packages/synapse-constants/constants/assets/icons/pls.svg deleted file mode 100644 index fccdbcd2ca..0000000000 --- a/packages/synapse-constants/constants/assets/icons/pls.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/sdt.svg b/packages/synapse-constants/constants/assets/icons/sdt.svg deleted file mode 100644 index 0d0824c16c..0000000000 --- a/packages/synapse-constants/constants/assets/icons/sdt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/sfi.svg b/packages/synapse-constants/constants/assets/icons/sfi.svg deleted file mode 100644 index e1a8cd0ad2..0000000000 --- a/packages/synapse-constants/constants/assets/icons/sfi.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/solar.svg b/packages/synapse-constants/constants/assets/icons/solar.svg deleted file mode 100644 index ed386e2dfa..0000000000 --- a/packages/synapse-constants/constants/assets/icons/solar.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/spectral.svg b/packages/synapse-constants/constants/assets/icons/spectral.svg deleted file mode 100644 index 86daa939ae..0000000000 --- a/packages/synapse-constants/constants/assets/icons/spectral.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/packages/synapse-constants/constants/assets/icons/susd.svg b/packages/synapse-constants/constants/assets/icons/susd.svg deleted file mode 100644 index 924acb5405..0000000000 --- a/packages/synapse-constants/constants/assets/icons/susd.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/sushi.svg b/packages/synapse-constants/constants/assets/icons/sushi.svg deleted file mode 100644 index bb4bc618fe..0000000000 --- a/packages/synapse-constants/constants/assets/icons/sushi.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/syn.svg b/packages/synapse-constants/constants/assets/icons/syn.svg deleted file mode 100644 index f7a230124d..0000000000 --- a/packages/synapse-constants/constants/assets/icons/syn.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/unidex.svg b/packages/synapse-constants/constants/assets/icons/unidex.svg deleted file mode 100644 index cf432ca520..0000000000 --- a/packages/synapse-constants/constants/assets/icons/unidex.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/usdb.png b/packages/synapse-constants/constants/assets/icons/usdb.png deleted file mode 100644 index 5e432e8e86..0000000000 Binary files a/packages/synapse-constants/constants/assets/icons/usdb.png and /dev/null differ diff --git a/packages/synapse-constants/constants/assets/icons/usdb.svg b/packages/synapse-constants/constants/assets/icons/usdb.svg deleted file mode 100644 index 397aae6629..0000000000 --- a/packages/synapse-constants/constants/assets/icons/usdb.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/packages/synapse-constants/constants/assets/icons/usdc.svg b/packages/synapse-constants/constants/assets/icons/usdc.svg deleted file mode 100644 index 0cf54945bd..0000000000 --- a/packages/synapse-constants/constants/assets/icons/usdc.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/usdt.svg b/packages/synapse-constants/constants/assets/icons/usdt.svg deleted file mode 100644 index 1ea97592e0..0000000000 --- a/packages/synapse-constants/constants/assets/icons/usdt.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/vsta.svg b/packages/synapse-constants/constants/assets/icons/vsta.svg deleted file mode 100644 index 5b2b1ac3c2..0000000000 --- a/packages/synapse-constants/constants/assets/icons/vsta.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/wbtc.svg b/packages/synapse-constants/constants/assets/icons/wbtc.svg deleted file mode 100644 index 7f9d25c59b..0000000000 --- a/packages/synapse-constants/constants/assets/icons/wbtc.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/icons/weth.svg b/packages/synapse-constants/constants/assets/icons/weth.svg deleted file mode 100644 index 0dec30bb5f..0000000000 --- a/packages/synapse-constants/constants/assets/icons/weth.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/synapse-constants/constants/assets/index.ts b/packages/synapse-constants/constants/assets/index.ts deleted file mode 100644 index f32ebc2c18..0000000000 --- a/packages/synapse-constants/constants/assets/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './chains' -export * from './explorer' -export * from './icons' diff --git a/packages/synapse-constants/constants/tokens/bridgeMap.ts b/packages/synapse-constants/constants/tokens/bridgeMap.ts deleted file mode 100644 index 70f9755d4a..0000000000 --- a/packages/synapse-constants/constants/tokens/bridgeMap.ts +++ /dev/null @@ -1,1999 +0,0 @@ -export const BRIDGE_MAP = { - '1': { - '0x0642026E7f0B6cCaC5925b4E7Fa61384250e1701': { - decimals: 18, - symbol: 'H2O', - origin: ['H2O'], - destination: ['H2O'], - swappable: [], - }, - '0x0ab87046fBb341D058F17CBC4c1133F25a20a52f': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0x0f2D719407FdBeFF09D87557AbB7232601FD9F29': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0x12f79f8c1A6e47a9b5F0796FDb008Bdc182fa19e': { - decimals: 18, - symbol: 'JEWEL', - origin: ['JEWEL'], - destination: ['JEWEL'], - swappable: [], - }, - '0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F': { - decimals: 18, - symbol: 'nUSD', - origin: [ - 'CCTP.USDC', - 'DAI', - 'RFQ.USDC', - 'USDC', - 'USDT', - 'nUSD', - 'synFRAX', - ], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x5f98805A4E8be255a32880FDeC7F6728C6568bA0', - '0x6B175474E89094C44Da98b954EedeAC495271d0F', - '0x6c3ea9036406852006290770BEdFcAbA0e23A0e8', - '0x853d955aCEf822Db058eb8505911ED77F175b99e', - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - '0xdAC17F958D2ee523a2206206994597C13D831ec7', - '0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E', - ], - }, - '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599': { - decimals: 8, - symbol: 'WBTC', - origin: ['WBTC'], - destination: ['WBTC'], - swappable: [], - }, - '0x514910771AF9Ca656af840dff83E8264EcF986CA': { - decimals: 18, - symbol: 'LINK', - origin: ['LINK'], - destination: ['LINK'], - swappable: [], - }, - '0x5f98805A4E8be255a32880FDeC7F6728C6568bA0': { - decimals: 18, - symbol: 'LUSD', - origin: [ - 'CCTP.USDC', - 'DAI', - 'RFQ.USDC', - 'USDC', - 'USDT', - 'nUSD', - 'synFRAX', - ], - destination: ['CCTP.USDC'], - swappable: [ - '0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F', - '0x6B175474E89094C44Da98b954EedeAC495271d0F', - '0x6c3ea9036406852006290770BEdFcAbA0e23A0e8', - '0x853d955aCEf822Db058eb8505911ED77F175b99e', - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - '0xdAC17F958D2ee523a2206206994597C13D831ec7', - '0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E', - ], - }, - '0x6982508145454Ce325dDbE47a25d4ec3d2311933': { - decimals: 18, - symbol: 'PEPE', - origin: ['PEPE'], - destination: ['PEPE'], - swappable: [], - }, - '0x6B175474E89094C44Da98b954EedeAC495271d0F': { - decimals: 18, - symbol: 'DAI', - origin: [ - 'CCTP.USDC', - 'DAI', - 'RFQ.USDC', - 'USDC', - 'USDT', - 'nUSD', - 'synFRAX', - ], - destination: ['CCTP.USDC', 'DAI', 'nUSD'], - swappable: [ - '0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F', - '0x5f98805A4E8be255a32880FDeC7F6728C6568bA0', - '0x6c3ea9036406852006290770BEdFcAbA0e23A0e8', - '0x853d955aCEf822Db058eb8505911ED77F175b99e', - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - '0xdAC17F958D2ee523a2206206994597C13D831ec7', - '0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E', - ], - }, - '0x6c3ea9036406852006290770BEdFcAbA0e23A0e8': { - decimals: 6, - symbol: 'PYUSD', - origin: [ - 'CCTP.USDC', - 'DAI', - 'RFQ.USDC', - 'USDC', - 'USDT', - 'nUSD', - 'synFRAX', - ], - destination: ['CCTP.USDC'], - swappable: [ - '0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F', - '0x5f98805A4E8be255a32880FDeC7F6728C6568bA0', - '0x6B175474E89094C44Da98b954EedeAC495271d0F', - '0x853d955aCEf822Db058eb8505911ED77F175b99e', - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - '0xdAC17F958D2ee523a2206206994597C13D831ec7', - '0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E', - ], - }, - '0x71Ab77b7dbB4fa7e017BC15090b2163221420282': { - decimals: 18, - symbol: 'HIGH', - origin: ['HIGH'], - destination: ['HIGH'], - swappable: [], - }, - '0x73968b9a57c6E53d41345FD57a6E6ae27d6CDB2F': { - decimals: 18, - symbol: 'SDT', - origin: ['SDT'], - destination: ['SDT'], - swappable: [], - }, - '0x853d955aCEf822Db058eb8505911ED77F175b99e': { - decimals: 18, - symbol: 'FRAX', - origin: [ - 'CCTP.USDC', - 'DAI', - 'RFQ.USDC', - 'USDC', - 'USDT', - 'nUSD', - 'synFRAX', - ], - destination: ['CCTP.USDC', 'synFRAX'], - swappable: [ - '0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F', - '0x5f98805A4E8be255a32880FDeC7F6728C6568bA0', - '0x6B175474E89094C44Da98b954EedeAC495271d0F', - '0x6c3ea9036406852006290770BEdFcAbA0e23A0e8', - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - '0xdAC17F958D2ee523a2206206994597C13D831ec7', - '0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E', - ], - }, - '0x98585dFc8d9e7D48F0b1aE47ce33332CF4237D96': { - decimals: 18, - symbol: 'NEWO', - origin: ['NEWO'], - destination: ['NEWO'], - swappable: [], - }, - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48': { - decimals: 6, - symbol: 'USDC', - origin: [ - 'CCTP.USDC', - 'DAI', - 'RFQ.USDC', - 'USDC', - 'USDT', - 'nUSD', - 'synFRAX', - ], - destination: ['CCTP.USDC', 'USDC', 'nUSD', 'RFQ.USDC'], - swappable: [ - '0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F', - '0x5f98805A4E8be255a32880FDeC7F6728C6568bA0', - '0x6B175474E89094C44Da98b954EedeAC495271d0F', - '0x6c3ea9036406852006290770BEdFcAbA0e23A0e8', - '0x853d955aCEf822Db058eb8505911ED77F175b99e', - '0xdAC17F958D2ee523a2206206994597C13D831ec7', - '0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E', - ], - }, - '0xA8d7F5e7C78ed0Fa097Cc5Ec66C1DC3104c9bbeb': { - decimals: 18, - symbol: 'VSTA', - origin: ['VSTA'], - destination: ['VSTA'], - swappable: [], - }, - '0xAdF7C35560035944e805D98fF17d58CDe2449389': { - decimals: 18, - symbol: 'SPEC', - origin: ['SPEC'], - destination: ['SPEC'], - swappable: [], - }, - '0xBAac2B4491727D78D2b78815144570b9f2Fe8899': { - decimals: 18, - symbol: 'DOG', - origin: ['DOG'], - destination: ['DOG'], - swappable: [], - }, - '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2': { - decimals: 18, - symbol: 'WETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH'], - swappable: [], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'ETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH', 'RFQ.ETH'], - swappable: [], - }, - '0xb753428af26E81097e7fD17f40c88aaA3E04902c': { - decimals: 18, - symbol: 'SFI', - origin: ['SFI'], - destination: ['SFI'], - swappable: [], - }, - '0xdAC17F958D2ee523a2206206994597C13D831ec7': { - decimals: 6, - symbol: 'USDT', - origin: [ - 'CCTP.USDC', - 'DAI', - 'RFQ.USDC', - 'USDC', - 'USDT', - 'nUSD', - 'synFRAX', - ], - destination: ['CCTP.USDC', 'USDT', 'nUSD'], - swappable: [ - '0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F', - '0x5f98805A4E8be255a32880FDeC7F6728C6568bA0', - '0x6B175474E89094C44Da98b954EedeAC495271d0F', - '0x6c3ea9036406852006290770BEdFcAbA0e23A0e8', - '0x853d955aCEf822Db058eb8505911ED77F175b99e', - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - '0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E', - ], - }, - '0xf0655DcEE37E5C0b70Fffd70D85f88F8eDf0AfF6': { - decimals: 18, - symbol: 'UNIDX', - origin: ['UNIDX'], - destination: ['UNIDX'], - swappable: [], - }, - '0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E': { - decimals: 18, - symbol: 'crvUSD', - origin: [ - 'CCTP.USDC', - 'DAI', - 'RFQ.USDC', - 'USDC', - 'USDT', - 'nUSD', - 'synFRAX', - ], - destination: ['CCTP.USDC'], - swappable: [ - '0x1B84765dE8B7566e4cEAF4D0fD3c5aF52D3DdE4F', - '0x5f98805A4E8be255a32880FDeC7F6728C6568bA0', - '0x6B175474E89094C44Da98b954EedeAC495271d0F', - '0x6c3ea9036406852006290770BEdFcAbA0e23A0e8', - '0x853d955aCEf822Db058eb8505911ED77F175b99e', - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - '0xdAC17F958D2ee523a2206206994597C13D831ec7', - ], - }, - }, - '10': { - '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85': { - decimals: 6, - symbol: 'USDC', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'RFQ.USDC'], - swappable: [ - '0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00', - '0x7F5c764cBc14f9669B88837ca1490cCa17c31607', - '0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9', - '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58', - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - ], - }, - '0x0b5740c6b4a97f90eF2F0220651Cca420B868FfB': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0x121ab82b49B2BC4c7901CA46B8277962b4350204': { - decimals: 18, - symbol: 'WETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH'], - swappable: [ - '0x809DC529f07651bD43A172e8dB6f4a7a0d771036', - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - ], - }, - '0x28b42698Caf46B4B012CF38b6C75867E0762186D': { - decimals: 18, - symbol: 'UNIDX', - origin: ['UNIDX'], - destination: ['UNIDX'], - swappable: [], - }, - '0x5A5fFf6F753d7C11A56A52FE47a177a87e431655': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00': { - decimals: 18, - symbol: 'nUSD', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', - '0x7F5c764cBc14f9669B88837ca1490cCa17c31607', - '0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9', - '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58', - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - ], - }, - '0x7F5c764cBc14f9669B88837ca1490cCa17c31607': { - decimals: 6, - symbol: 'USDC.e', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', - '0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00', - '0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9', - '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58', - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - ], - }, - '0x809DC529f07651bD43A172e8dB6f4a7a0d771036': { - decimals: 18, - symbol: 'nETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH'], - swappable: [ - '0x121ab82b49B2BC4c7901CA46B8277962b4350204', - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - ], - }, - '0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9': { - decimals: 18, - symbol: 'sUSD', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC'], - swappable: [ - '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', - '0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00', - '0x7F5c764cBc14f9669B88837ca1490cCa17c31607', - '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58', - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - ], - }, - '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58': { - decimals: 6, - symbol: 'USDT', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC'], - swappable: [ - '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', - '0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00', - '0x7F5c764cBc14f9669B88837ca1490cCa17c31607', - '0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9', - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - ], - }, - '0xD9eAA386cCD65F30b77FF175F6b52115FE454fD6': { - decimals: 18, - symbol: 'PLS', - origin: ['PLS'], - destination: ['PLS'], - swappable: [], - }, - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1': { - decimals: 18, - symbol: 'DAI', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC'], - swappable: [ - '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', - '0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00', - '0x7F5c764cBc14f9669B88837ca1490cCa17c31607', - '0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9', - '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58', - ], - }, - '0xE3c82A836Ec85311a433fBd9486EfAF4b1AFbF48': { - decimals: 18, - symbol: 'H2O', - origin: ['H2O'], - destination: ['H2O'], - swappable: [], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'ETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH', 'RFQ.ETH'], - swappable: [ - '0x121ab82b49B2BC4c7901CA46B8277962b4350204', - '0x809DC529f07651bD43A172e8dB6f4a7a0d771036', - ], - }, - '0xd52f94DF742a6F4B4C8b033369fE13A41782Bf44': { - decimals: 18, - symbol: 'L2DAO', - origin: ['L2DAO'], - destination: ['L2DAO'], - swappable: [], - }, - }, - '25': { - '0x396c9c192dd323995346632581BEF92a31AC623b': { - decimals: 18, - symbol: 'nUSD', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: ['0xc21223249CA28397B4B6541dfFaEcC539BfF0c59'], - }, - '0xFD0F80899983b8D46152aa1717D76cba71a31616': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0xbB0A63A6CA2071c6C4bcAC11a1A317b20E3E999C': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0xc21223249CA28397B4B6541dfFaEcC539BfF0c59': { - decimals: 6, - symbol: 'USDC', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: ['0x396c9c192dd323995346632581BEF92a31AC623b'], - }, - }, - '56': { - '0x03eFca7CEb108734D3777684F3C0A0d8ad652f79': { - decimals: 18, - symbol: 'H2O', - origin: ['H2O'], - destination: ['H2O'], - swappable: [], - }, - '0x0FE9778c005a5A6115cBE12b0568a2d50b765A51': { - decimals: 18, - symbol: 'NFD', - origin: ['NFD'], - destination: ['NFD'], - swappable: [], - }, - '0x130025eE738A66E691E6A7a62381CB33c6d9Ae83': { - decimals: 18, - symbol: 'JUMP', - origin: ['JUMP'], - destination: ['JUMP'], - swappable: [], - }, - '0x23b891e5C62E0955ae2bD185990103928Ab817b3': { - decimals: 18, - symbol: 'nUSD', - origin: ['BUSD', 'nUSD'], - destination: ['nUSD'], - swappable: [ - '0x55d398326f99059fF775485246999027B3197955', - '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', - '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56', - ], - }, - '0x55d398326f99059fF775485246999027B3197955': { - decimals: 18, - symbol: 'USDT', - origin: ['BUSD', 'nUSD'], - destination: ['nUSD'], - swappable: [ - '0x23b891e5C62E0955ae2bD185990103928Ab817b3', - '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', - '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56', - ], - }, - '0x5f4Bde007Dc06b867f86EBFE4802e34A1fFEEd63': { - decimals: 18, - symbol: 'HIGH', - origin: ['HIGH'], - destination: ['HIGH'], - swappable: [], - }, - '0x88918495892BAF4536611E38E75D771Dc6Ec0863': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d': { - decimals: 18, - symbol: 'USDC', - origin: ['BUSD', 'nUSD'], - destination: ['nUSD'], - swappable: [ - '0x23b891e5C62E0955ae2bD185990103928Ab817b3', - '0x55d398326f99059fF775485246999027B3197955', - '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56', - ], - }, - '0xa4080f1778e69467E905B8d6F72f6e441f9e9484': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0xaA88C603d142C371eA0eAC8756123c5805EdeE03': { - decimals: 18, - symbol: 'DOG', - origin: ['DOG'], - destination: ['DOG'], - swappable: [], - }, - '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56': { - decimals: 18, - symbol: 'BUSD', - origin: ['BUSD', 'nUSD'], - destination: ['BUSD', 'nUSD'], - swappable: [ - '0x23b891e5C62E0955ae2bD185990103928Ab817b3', - '0x55d398326f99059fF775485246999027B3197955', - '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', - ], - }, - }, - '137': { - '0x0A5926027d407222F8fe20f24cB16e103f617046': { - decimals: 18, - symbol: 'NFD', - origin: ['NFD'], - destination: ['NFD'], - swappable: [], - }, - '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270': { - decimals: 18, - symbol: 'WMATIC', - origin: ['MATIC'], - destination: ['MATIC'], - swappable: [], - }, - '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174': { - decimals: 6, - symbol: 'USDC.e', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', - '0x45c32fA6DF82ead1e2EF74d17b76547EDdFaFF89', - '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', - '0xB6c473756050dE474286bED418B77Aeac39B02aF', - '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', - ], - }, - '0x32ba7cF7d681357529013de6a2CDF93933C0dF3f': { - decimals: 18, - symbol: 'H2O', - origin: ['H2O'], - destination: ['H2O'], - swappable: [], - }, - '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359': { - decimals: 6, - symbol: 'USDC', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC'], - swappable: [ - '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', - '0x45c32fA6DF82ead1e2EF74d17b76547EDdFaFF89', - '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', - '0xB6c473756050dE474286bED418B77Aeac39B02aF', - '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', - ], - }, - '0x45c32fA6DF82ead1e2EF74d17b76547EDdFaFF89': { - decimals: 18, - symbol: 'FRAX', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC'], - swappable: [ - '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', - '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', - '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', - '0xB6c473756050dE474286bED418B77Aeac39B02aF', - '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', - ], - }, - '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063': { - decimals: 18, - symbol: 'DAI', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', - '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', - '0x45c32fA6DF82ead1e2EF74d17b76547EDdFaFF89', - '0xB6c473756050dE474286bED418B77Aeac39B02aF', - '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', - ], - }, - '0xB6c473756050dE474286bED418B77Aeac39B02aF': { - decimals: 18, - symbol: 'nUSD', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', - '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', - '0x45c32fA6DF82ead1e2EF74d17b76547EDdFaFF89', - '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', - '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', - ], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'MATIC', - origin: ['MATIC'], - destination: ['MATIC'], - swappable: [], - }, - '0xc2132D05D31c914a87C6611C10748AEb04B58e8F': { - decimals: 6, - symbol: 'USDT', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', - '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', - '0x45c32fA6DF82ead1e2EF74d17b76547EDdFaFF89', - '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', - '0xB6c473756050dE474286bED418B77Aeac39B02aF', - ], - }, - '0xd8cA34fd379d9ca3C6Ee3b3905678320F5b45195': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0xeEe3371B89FC43Ea970E908536Fcddd975135D8a': { - decimals: 18, - symbol: 'DOG', - origin: ['DOG'], - destination: ['DOG'], - swappable: [], - }, - '0xf8F9efC0db77d8881500bb06FF5D6ABc3070E695': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - }, - '250': { - '0x0483a76D80D0aFEC6bd2afd12C1AD865b9DF1471': { - decimals: 18, - symbol: 'UNIDX', - origin: ['UNIDX'], - destination: ['UNIDX'], - swappable: [], - }, - '0x1852F70512298d56e9c8FDd905e02581E04ddb2a': { - decimals: 18, - symbol: 'synFRAX', - origin: ['synFRAX'], - destination: ['synFRAX'], - swappable: [], - }, - '0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83': { - decimals: 18, - symbol: 'WFTM', - origin: ['FTM'], - destination: ['FTM'], - swappable: [], - }, - '0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00': { - decimals: 18, - symbol: 'nETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: [], - }, - '0x78DE9326792ce1d6eCA0c978753c6953Cdeedd73': { - decimals: 18, - symbol: 'JUMP', - origin: ['JUMP'], - destination: ['JUMP'], - swappable: [], - }, - '0x91fa20244Fb509e8289CA630E5db3E9166233FDc': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0xE3c82A836Ec85311a433fBd9486EfAF4b1AFbF48': { - decimals: 18, - symbol: 'SDT', - origin: ['SDT'], - destination: ['SDT'], - swappable: [], - }, - '0xE55e19Fb4F2D85af758950957714292DAC1e25B2': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0xED2a7edd7413021d440b09D654f3b87712abAB66': { - decimals: 18, - symbol: 'nUSD', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'FTM', - origin: ['FTM'], - destination: ['FTM'], - swappable: [], - }, - }, - '288': { - '0x5DE1677344D3Cb0D7D465c10b72A8f60699C062d': { - decimals: 6, - symbol: 'USDT', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x66a2A913e447d6b4BF33EFbec43aAeF87890FBbc', - '0x6B4712AE9797C199edd44F897cA09BC57628a1CF', - '0xf74195Bb8a5cf652411867c5C2C5b8C2a402be35', - ], - }, - '0x66a2A913e447d6b4BF33EFbec43aAeF87890FBbc': { - decimals: 6, - symbol: 'USDC', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x5DE1677344D3Cb0D7D465c10b72A8f60699C062d', - '0x6B4712AE9797C199edd44F897cA09BC57628a1CF', - '0xf74195Bb8a5cf652411867c5C2C5b8C2a402be35', - ], - }, - '0x6B4712AE9797C199edd44F897cA09BC57628a1CF': { - decimals: 18, - symbol: 'nUSD', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x5DE1677344D3Cb0D7D465c10b72A8f60699C062d', - '0x66a2A913e447d6b4BF33EFbec43aAeF87890FBbc', - '0xf74195Bb8a5cf652411867c5C2C5b8C2a402be35', - ], - }, - '0x96419929d7949D6A801A6909c145C8EEf6A40431': { - decimals: 18, - symbol: 'nETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: [ - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - '0xd203De32170130082896b4111eDF825a4774c18E', - ], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'ETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: [ - '0x96419929d7949D6A801A6909c145C8EEf6A40431', - '0xd203De32170130082896b4111eDF825a4774c18E', - ], - }, - '0xb554A55358fF0382Fb21F0a478C3546d1106Be8c': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0xd203De32170130082896b4111eDF825a4774c18E': { - decimals: 18, - symbol: 'WETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: [ - '0x96419929d7949D6A801A6909c145C8EEf6A40431', - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - ], - }, - '0xd22C0a4Af486C7FA08e282E9eB5f30F9AaA62C95': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0xf74195Bb8a5cf652411867c5C2C5b8C2a402be35': { - decimals: 18, - symbol: 'DAI', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x5DE1677344D3Cb0D7D465c10b72A8f60699C062d', - '0x66a2A913e447d6b4BF33EFbec43aAeF87890FBbc', - '0x6B4712AE9797C199edd44F897cA09BC57628a1CF', - ], - }, - }, - '1088': { - '0x17C09cfC96C865CF546d73365Cedb6dC66986963': { - decimals: 18, - symbol: 'JEWEL', - origin: ['JEWEL'], - destination: ['JEWEL'], - swappable: [], - }, - '0x420000000000000000000000000000000000000A': { - decimals: 18, - symbol: 'WETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: ['0x931B8f17764362A3325D30681009f0eDd6211231'], - }, - '0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0x931B8f17764362A3325D30681009f0eDd6211231': { - decimals: 18, - symbol: 'nETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: ['0x420000000000000000000000000000000000000A'], - }, - '0x961318Fc85475E125B99Cc9215f62679aE5200aB': { - decimals: 18, - symbol: 'nUSD', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: ['0xEA32A96608495e54156Ae48931A7c20f0dcc1a21'], - }, - '0xE3c82A836Ec85311a433fBd9486EfAF4b1AFbF48': { - decimals: 18, - symbol: 'JUMP', - origin: ['JUMP'], - destination: ['JUMP'], - swappable: [], - }, - '0xEA32A96608495e54156Ae48931A7c20f0dcc1a21': { - decimals: 6, - symbol: 'm.USDC', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: ['0x961318Fc85475E125B99Cc9215f62679aE5200aB'], - }, - '0xFB21B70922B9f6e3C6274BcD6CB1aa8A0fe20B80': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - }, - '1284': { - '0x0DB6729C03C85B0708166cA92801BcB5CAc781fC': { - decimals: 18, - symbol: 'veSOLAR', - origin: ['veSOLAR'], - destination: ['veSOLAR'], - swappable: [], - }, - '0x1d4C2a246311bB9f827F4C768e277FF5787B7D7E': { - decimals: 18, - symbol: 'MOVR', - origin: ['MOVR'], - destination: ['MOVR'], - swappable: [], - }, - '0x3192Ae73315c3634Ffa217f71CF6CBc30FeE349A': { - decimals: 18, - symbol: 'WETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: [], - }, - '0xA1f8890E39b4d8E33efe296D698fe42Fb5e59cC3': { - decimals: 18, - symbol: 'AVAX', - origin: ['AVAX'], - destination: ['AVAX'], - swappable: [], - }, - '0xA46aDF6D5881ca0b8596EDadF8f058F8c16d8B68': { - decimals: 18, - symbol: 'H2O', - origin: ['H2O'], - destination: ['H2O'], - swappable: [], - }, - '0xD2666441443DAa61492FFe0F37717578714a4521': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0xDd47A348AB60c61Ad6B60cA8C31ea5e00eBfAB4F': { - decimals: 18, - symbol: 'synFRAX', - origin: ['synFRAX'], - destination: ['synFRAX'], - swappable: [], - }, - '0xF44938b0125A6662f9536281aD2CD6c499F22004': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - }, - '1285': { - '0x3bF21Ce864e58731B6f28D68d5928BcBEb0Ad172': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0x76906411D07815491A5E577022757aD941fb5066': { - decimals: 18, - symbol: 'veSOLAR', - origin: ['veSOLAR'], - destination: ['veSOLAR'], - swappable: [], - }, - '0x98878B06940aE243284CA214f92Bb71a2b032B8A': { - decimals: 18, - symbol: 'WMOVR', - origin: ['MOVR'], - destination: ['MOVR'], - swappable: [], - }, - '0x9c0a820bb01e2807aCcd1c682d359e92DDd41403': { - decimals: 18, - symbol: 'H2O', - origin: ['H2O'], - destination: ['H2O'], - swappable: [], - }, - '0xE96AC70907ffF3Efee79f502C985A7A21Bce407d': { - decimals: 18, - symbol: 'synFRAX', - origin: ['synFRAX'], - destination: ['synFRAX'], - swappable: [], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'MOVR', - origin: ['MOVR'], - destination: ['MOVR'], - swappable: [], - }, - '0xd80d8688b02B3FD3afb81cDb124F188BB5aD0445': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - }, - '2000': { - '0x10D70831f9C3c11c5fe683b2f1Be334503880DB6': { - decimals: 18, - symbol: 'FRAX', - origin: ['synFRAX'], - destination: ['synFRAX'], - swappable: [], - }, - '0x1555C68Be3b22cdcCa934Ae88Cb929Db40aB311d': { - decimals: 18, - symbol: 'BUSD', - origin: ['BUSD'], - destination: ['BUSD'], - swappable: [], - }, - '0x7f8e71DD5A7e445725F0EF94c7F01806299e877A': { - decimals: 6, - symbol: 'USDT', - origin: ['USDT'], - destination: ['USDT'], - swappable: [], - }, - '0x85C2D3bEBffD83025910985389aB8aD655aBC946': { - decimals: 6, - symbol: 'USDC', - origin: ['USDC'], - destination: ['USDC'], - swappable: [], - }, - '0x868055ADFA27D331d5b69b1BF3469aDAAc3ba7f2': { - decimals: 18, - symbol: 'NFD', - origin: ['NFD'], - destination: ['NFD'], - swappable: [], - }, - '0x9F4614E4Ea4A0D7c4B1F946057eC030beE416cbB': { - decimals: 18, - symbol: 'WETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: [], - }, - '0xB3306f03595490e5cC3a1b1704a5a158D3436ffC': { - decimals: 18, - symbol: 'DAI', - origin: ['DAI'], - destination: ['DAI'], - swappable: [], - }, - '0xD0c6179c43C00221915f1a61f8eC06A5Aa32F9EC': { - decimals: 8, - symbol: 'WBTC', - origin: ['WBTC'], - destination: ['WBTC'], - swappable: [], - }, - '0xDfA53EeBA61D69E1D2b56b36d78449368F0265c1': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - }, - '7700': { - '0x09fEC30669d63A13c666d2129230dD5588E2e240': { - decimals: 18, - symbol: 'nETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: [], - }, - '0x4e71A2E537B7f9D9413D3991D37958c0b5e1e503': { - decimals: 18, - symbol: 'NOTE', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x80b5a32E4F032B2a058b4F29EC95EEfEEB87aDcd', - '0xD8836aF2e565D3Befce7D906Af63ee45a57E8f80', - '0xd567B3d7B8FE3C79a1AD8dA978812cfC4Fa05e75', - ], - }, - '0x555982d2E211745b96736665e19D9308B615F78e': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0x80b5a32E4F032B2a058b4F29EC95EEfEEB87aDcd': { - decimals: 6, - symbol: 'USDC', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x4e71A2E537B7f9D9413D3991D37958c0b5e1e503', - '0xD8836aF2e565D3Befce7D906Af63ee45a57E8f80', - '0xd567B3d7B8FE3C79a1AD8dA978812cfC4Fa05e75', - ], - }, - '0xD8836aF2e565D3Befce7D906Af63ee45a57E8f80': { - decimals: 18, - symbol: 'nUSD', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x4e71A2E537B7f9D9413D3991D37958c0b5e1e503', - '0x80b5a32E4F032B2a058b4F29EC95EEfEEB87aDcd', - '0xd567B3d7B8FE3C79a1AD8dA978812cfC4Fa05e75', - ], - }, - '0xd567B3d7B8FE3C79a1AD8dA978812cfC4Fa05e75': { - decimals: 6, - symbol: 'USDT', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x4e71A2E537B7f9D9413D3991D37958c0b5e1e503', - '0x80b5a32E4F032B2a058b4F29EC95EEfEEB87aDcd', - '0xD8836aF2e565D3Befce7D906Af63ee45a57E8f80', - ], - }, - }, - '8217': { - '0x078dB7827a5531359f6CB63f62CFA20183c4F10c': { - decimals: 18, - symbol: 'DAI', - origin: ['DAI'], - destination: ['DAI'], - swappable: [], - }, - '0x30C103f8f5A3A732DFe2dCE1Cc9446f545527b43': { - decimals: 18, - symbol: 'JEWEL', - origin: ['JEWEL'], - destination: ['JEWEL'], - swappable: [], - }, - '0x5819b6af194A78511c79C85Ea68D2377a7e9335f': { - decimals: 18, - symbol: 'WKLAY', - origin: ['KLAY'], - destination: ['KLAY'], - swappable: [], - }, - '0x6270B58BE569a7c0b8f47594F191631Ae5b2C86C': { - decimals: 6, - symbol: 'USDC', - origin: ['USDC'], - destination: ['USDC'], - swappable: [], - }, - '0xCD6f29dC9Ca217d0973d3D21bF58eDd3CA871a86': { - decimals: 18, - symbol: 'WETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: [], - }, - '0xCd8fE44A29Db9159dB36f96570d7A4d91986f528': { - decimals: 18, - symbol: 'AVAX', - origin: ['AVAX'], - destination: ['AVAX'], - swappable: [], - }, - '0xDCbacF3f7a069922E677912998c8d57423C37dfA': { - decimals: 8, - symbol: 'WBTC', - origin: ['WBTC'], - destination: ['WBTC'], - swappable: [], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'KLAY', - origin: ['KLAY'], - destination: ['KLAY'], - swappable: [], - }, - '0xd6dAb4CfF47dF175349e6e7eE2BF7c40Bb8C05A3': { - decimals: 6, - symbol: 'USDT', - origin: ['USDT'], - destination: ['USDT'], - swappable: [], - }, - '0xe82f87ba4E97b2796aA0Fa4eFB06e8f0d2EB4FE1': { - decimals: 8, - symbol: 'BTC.b', - origin: ['BTCB'], - destination: ['BTCB'], - swappable: [], - }, - '0xfbEd1AbB3aD0f8C467068De9fDE905887e8C9118': { - decimals: 18, - symbol: 'LINK', - origin: ['LINK'], - destination: ['LINK'], - swappable: [], - }, - }, - '8453': { - '0x417Ac0e078398C154EdFadD9Ef675d30Be60Af93': { - decimals: 18, - symbol: 'crvUSD', - origin: ['CCTP.USDC', 'RFQ.USDC'], - destination: ['CCTP.USDC'], - swappable: [ - '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb', - '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', - '0xEB466342C4d449BC9f53A865D5Cb90586f405215', - '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA', - ], - }, - '0x4200000000000000000000000000000000000006': { - decimals: 18, - symbol: 'WETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH'], - swappable: [ - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - '0xb554A55358fF0382Fb21F0a478C3546d1106Be8c', - ], - }, - '0x432036208d2717394d2614d6697c46DF3Ed69540': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb': { - decimals: 18, - symbol: 'DAI', - origin: ['CCTP.USDC', 'RFQ.USDC'], - destination: ['CCTP.USDC'], - swappable: [ - '0x417Ac0e078398C154EdFadD9Ef675d30Be60Af93', - '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', - '0xEB466342C4d449BC9f53A865D5Cb90586f405215', - '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA', - ], - }, - '0x6B4712AE9797C199edd44F897cA09BC57628a1CF': { - decimals: 18, - symbol: 'UNIDX', - origin: ['UNIDX'], - destination: ['UNIDX'], - swappable: [], - }, - '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913': { - decimals: 6, - symbol: 'USDC', - origin: ['CCTP.USDC', 'RFQ.USDC'], - destination: ['CCTP.USDC', 'RFQ.USDC'], - swappable: [ - '0x417Ac0e078398C154EdFadD9Ef675d30Be60Af93', - '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb', - '0xEB466342C4d449BC9f53A865D5Cb90586f405215', - '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA', - ], - }, - '0x96419929d7949D6A801A6909c145C8EEf6A40431': { - decimals: 18, - symbol: 'SPEC', - origin: ['SPEC'], - destination: ['SPEC'], - swappable: [], - }, - '0xEB466342C4d449BC9f53A865D5Cb90586f405215': { - decimals: 6, - symbol: 'axlUSDC', - origin: ['CCTP.USDC', 'RFQ.USDC'], - destination: ['CCTP.USDC'], - swappable: [ - '0x417Ac0e078398C154EdFadD9Ef675d30Be60Af93', - '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb', - '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', - '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA', - ], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'ETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH', 'RFQ.ETH'], - swappable: [ - '0x4200000000000000000000000000000000000006', - '0xb554A55358fF0382Fb21F0a478C3546d1106Be8c', - ], - }, - '0xb554A55358fF0382Fb21F0a478C3546d1106Be8c': { - decimals: 18, - symbol: 'nETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH'], - swappable: [ - '0x4200000000000000000000000000000000000006', - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - ], - }, - '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA': { - decimals: 6, - symbol: 'USDbC', - origin: ['CCTP.USDC', 'RFQ.USDC'], - destination: ['CCTP.USDC'], - swappable: [ - '0x417Ac0e078398C154EdFadD9Ef675d30Be60Af93', - '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb', - '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', - '0xEB466342C4d449BC9f53A865D5Cb90586f405215', - ], - }, - }, - '42161': { - '0x080F6AEd32Fc474DD5717105Dba5ea57268F46eb': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0x0877154a755B24D499B8e2bD7ecD54d3c92BA433': { - decimals: 18, - symbol: 'NEWO', - origin: ['NEWO'], - destination: ['NEWO'], - swappable: [], - }, - '0x087d18A77465c34CDFd3a081a2504b7E86CE4EF8': { - decimals: 18, - symbol: 'SDT', - origin: ['SDT'], - destination: ['SDT'], - swappable: [], - }, - '0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F': { - decimals: 18, - symbol: 'FRAX', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC'], - swappable: [ - '0x2913E812Cf0dcCA30FB28E6Cac3d2DCFF4497688', - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', - '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', - '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', - ], - }, - '0x2913E812Cf0dcCA30FB28E6Cac3d2DCFF4497688': { - decimals: 18, - symbol: 'nUSD', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F', - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', - '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', - '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', - ], - }, - '0x2CaB3abfC1670D1a452dF502e216a66883cDf079': { - decimals: 18, - symbol: 'L2DAO', - origin: ['L2DAO'], - destination: ['L2DAO'], - swappable: [], - }, - '0x3ea9B0ab55F34Fb188824Ee288CeaEfC63cf908e': { - decimals: 18, - symbol: 'nETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH'], - swappable: [ - '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - ], - }, - '0x51318B7D00db7ACc4026C88c3952B66278B6A67F': { - decimals: 18, - symbol: 'PLS', - origin: ['PLS'], - destination: ['PLS'], - swappable: [], - }, - '0x5429706887FCb58a595677B73E9B0441C25d993D': { - decimals: 18, - symbol: 'UNIDX', - origin: ['UNIDX'], - destination: ['UNIDX'], - swappable: [], - }, - '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1': { - decimals: 18, - symbol: 'WETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH'], - swappable: [ - '0x3ea9B0ab55F34Fb188824Ee288CeaEfC63cf908e', - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - ], - }, - '0x8D9bA570D6cb60C7e3e0F31343Efe75AB8E65FB1': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0xA54B8e178A49F8e5405A4d44Bb31F496e5564A05': { - decimals: 18, - symbol: 'PEPE', - origin: ['PEPE'], - destination: ['PEPE'], - swappable: [], - }, - '0xD1c6f989e9552DB523aBAE2378227fBb059a3976': { - decimals: 18, - symbol: 'H2O', - origin: ['H2O'], - destination: ['H2O'], - swappable: [], - }, - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1': { - decimals: 18, - symbol: 'DAI', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC'], - swappable: [ - '0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F', - '0x2913E812Cf0dcCA30FB28E6Cac3d2DCFF4497688', - '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', - '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', - '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', - ], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'ETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH', 'RFQ.ETH'], - swappable: [ - '0x3ea9B0ab55F34Fb188824Ee288CeaEfC63cf908e', - '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', - ], - }, - '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8': { - decimals: 6, - symbol: 'USDC.e', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F', - '0x2913E812Cf0dcCA30FB28E6Cac3d2DCFF4497688', - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', - '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', - ], - }, - '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9': { - decimals: 6, - symbol: 'USDT', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F', - '0x2913E812Cf0dcCA30FB28E6Cac3d2DCFF4497688', - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', - '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', - ], - }, - '0xa684cd057951541187f288294a1e1C2646aA2d24': { - decimals: 18, - symbol: 'VSTA', - origin: ['VSTA'], - destination: ['VSTA'], - swappable: [], - }, - '0xaf88d065e77c8cC2239327C5EDb3A432268e5831': { - decimals: 6, - symbol: 'USDC', - origin: ['CCTP.USDC', 'RFQ.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'RFQ.USDC'], - swappable: [ - '0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F', - '0x2913E812Cf0dcCA30FB28E6Cac3d2DCFF4497688', - '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', - '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', - '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', - ], - }, - '0xfc5A1A6EB076a2C7aD06eD22C90d7E710E35ad0a': { - decimals: 18, - symbol: 'GMX', - origin: ['GMX'], - destination: ['GMX'], - swappable: [], - }, - }, - '43114': { - '0x152b9d0FdC40C096757F570A51E494bd4b943E50': { - decimals: 8, - symbol: 'BTC.b', - origin: ['BTCB'], - destination: ['BTCB'], - swappable: [], - }, - '0x19E1ae0eE35c0404f835521146206595d37981ae': { - decimals: 18, - symbol: 'nETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: ['0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB'], - }, - '0x1f1E7c893855525b303f99bDF5c3c05Be09ca251': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0x321E7092a180BB43555132ec53AaA65a5bF84251': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB': { - decimals: 18, - symbol: 'WETH.e', - origin: ['nETH'], - destination: ['nETH'], - swappable: ['0x19E1ae0eE35c0404f835521146206595d37981ae'], - }, - '0x4Bfc90322dD638F81F034517359BD447f8E0235a': { - decimals: 18, - symbol: 'NEWO', - origin: ['NEWO'], - destination: ['NEWO'], - swappable: [], - }, - '0x62edc0692BD897D2295872a9FFCac5425011c661': { - decimals: 18, - symbol: 'GMX', - origin: ['GMX'], - destination: ['GMX'], - swappable: [], - }, - '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7': { - decimals: 6, - symbol: 'USDT', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664', - '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', - '0xCFc37A6AB183dd4aED08C204D1c2773c0b1BDf46', - '0xc7198437980c041c805A1EDcbA50c1Ce5db95118', - '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70', - ], - }, - '0x997Ddaa07d716995DE90577C123Db411584E5E46': { - decimals: 18, - symbol: 'JEWEL', - origin: ['JEWEL'], - destination: ['JEWEL'], - swappable: [], - }, - '0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664': { - decimals: 6, - symbol: 'USDC.e', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC'], - swappable: [ - '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7', - '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', - '0xCFc37A6AB183dd4aED08C204D1c2773c0b1BDf46', - '0xc7198437980c041c805A1EDcbA50c1Ce5db95118', - '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70', - ], - }, - '0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7': { - decimals: 18, - symbol: 'WAVAX', - origin: ['AVAX'], - destination: ['AVAX'], - swappable: [], - }, - '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E': { - decimals: 6, - symbol: 'USDC', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7', - '0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664', - '0xCFc37A6AB183dd4aED08C204D1c2773c0b1BDf46', - '0xc7198437980c041c805A1EDcbA50c1Ce5db95118', - '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70', - ], - }, - '0xC6b11a4Fd833d1117E9D312c02865647cd961107': { - decimals: 18, - symbol: 'H2O', - origin: ['H2O'], - destination: ['H2O'], - swappable: [], - }, - '0xCCBf7c451F81752F7d2237F2c18C371E6e089E69': { - decimals: 18, - symbol: 'SDT', - origin: ['SDT'], - destination: ['SDT'], - swappable: [], - }, - '0xCFc37A6AB183dd4aED08C204D1c2773c0b1BDf46': { - decimals: 18, - symbol: 'nUSD', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC', 'nUSD'], - swappable: [ - '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7', - '0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664', - '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', - '0xc7198437980c041c805A1EDcbA50c1Ce5db95118', - '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70', - ], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'AVAX', - origin: ['AVAX'], - destination: ['AVAX'], - swappable: [], - }, - '0xc2Bf0A1f7D8Da50D608bc96CF701110d4A438312': { - decimals: 18, - symbol: 'SFI', - origin: ['SFI'], - destination: ['SFI'], - swappable: [], - }, - '0xc7198437980c041c805A1EDcbA50c1Ce5db95118': { - decimals: 6, - symbol: 'USDT.e', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC'], - swappable: [ - '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7', - '0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664', - '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', - '0xCFc37A6AB183dd4aED08C204D1c2773c0b1BDf46', - '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70', - ], - }, - '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70': { - decimals: 18, - symbol: 'DAI.e', - origin: ['CCTP.USDC', 'nUSD'], - destination: ['CCTP.USDC'], - swappable: [ - '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7', - '0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664', - '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', - '0xCFc37A6AB183dd4aED08C204D1c2773c0b1BDf46', - '0xc7198437980c041c805A1EDcbA50c1Ce5db95118', - ], - }, - '0xf1293574EE43950E7a8c9F1005Ff097A9A713959': { - decimals: 18, - symbol: 'NFD', - origin: ['NFD'], - destination: ['NFD'], - swappable: [], - }, - }, - '53935': { - '0x2Df041186C844F8a2e2b63F16145Bc6Ff7d23E25': { - decimals: 18, - symbol: 'FTM', - origin: ['FTM'], - destination: ['FTM'], - swappable: [], - }, - '0x3AD9DFE640E1A9Cc1D9B0948620820D975c3803a': { - decimals: 18, - symbol: 'USDC', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [], - }, - '0x7516EB8B8Edfa420f540a162335eACF3ea05a247': { - decimals: 8, - symbol: 'BTC.b', - origin: ['BTCB'], - destination: ['BTCB'], - swappable: [], - }, - '0x77f2656d04E158f915bC22f07B779D94c1DC47Ff': { - decimals: 18, - symbol: 'xJEWEL', - origin: ['xJEWEL'], - destination: ['xJEWEL'], - swappable: [], - }, - '0x97855Ba65aa7ed2F65Ed832a776537268158B78a': { - decimals: 18, - symbol: 'KLAY', - origin: ['KLAY'], - destination: ['KLAY'], - swappable: [], - }, - '0xB57B60DeBDB0b8172bb6316a9164bd3C695F133a': { - decimals: 18, - symbol: 'AVAX', - origin: ['AVAX'], - destination: ['AVAX'], - swappable: [], - }, - '0xCCb93dABD71c8Dad03Fc4CE5559dC3D89F67a260': { - decimals: 18, - symbol: 'WJEWEL', - origin: ['JEWEL'], - destination: ['JEWEL'], - swappable: [], - }, - '0xD17a41Cd199edF1093A9Be4404EaDe52Ec19698e': { - decimals: 18, - symbol: 'MATIC', - origin: ['MATIC'], - destination: ['MATIC'], - swappable: [], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'JEWEL', - origin: ['JEWEL'], - destination: ['JEWEL'], - swappable: [], - }, - '0xfBDF0E31808d0aa7b9509AA6aBC9754E48C58852': { - decimals: 18, - symbol: 'ETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: [], - }, - }, - '59144': { - '0x176211869cA2b568f2A7D4EE941E073a821EE1ff': { - origin: ['RFQ.USDC'], - destination: ['RFQ.USDC'], - swappable: [], - symbol: 'USDC', - decimals: 6, - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - origin: ['RFQ.ETH'], - destination: ['RFQ.ETH'], - swappable: [], - symbol: 'ETH', - decimals: 18, - }, - }, - '81457': { - '0x3194B0A295D87fDAA54DF852c248F7a6BAF6c6e0': { - decimals: 18, - symbol: 'nUSD', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: ['0x4300000000000000000000000000000000000003'], - }, - '0x4300000000000000000000000000000000000003': { - decimals: 18, - symbol: 'USDB', - origin: ['nUSD', 'RFQ.USDB'], - destination: ['nUSD', 'RFQ.USDB'], - swappable: ['0x3194B0A295D87fDAA54DF852c248F7a6BAF6c6e0'], - }, - '0x4300000000000000000000000000000000000004': { - decimals: 18, - symbol: 'WETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH'], - swappable: [ - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - '0xce971282fAAc9faBcF121944956da7142cccC855', - ], - }, - '0x9592f08387134e218327E6E8423400eb845EdE0E': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - decimals: 18, - symbol: 'ETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH', 'RFQ.ETH'], - swappable: [ - '0x4300000000000000000000000000000000000004', - '0xce971282fAAc9faBcF121944956da7142cccC855', - ], - }, - '0xce971282fAAc9faBcF121944956da7142cccC855': { - decimals: 18, - symbol: 'nETH', - origin: ['RFQ.ETH', 'nETH'], - destination: ['nETH'], - swappable: [ - '0x4300000000000000000000000000000000000004', - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - ], - }, - }, - '534352': { - '0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4': { - origin: ['RFQ.USDC'], - destination: ['RFQ.USDC'], - swappable: [], - symbol: 'USDC', - decimals: 6, - }, - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { - origin: ['RFQ.ETH'], - destination: ['RFQ.ETH'], - swappable: [], - symbol: 'ETH', - decimals: 18, - }, - }, - '1313161554': { - '0x07379565cD8B0CaE7c60Dc78e7f601b34AF2A21c': { - decimals: 18, - symbol: 'nUSD', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x4988a896b1227218e4A686fdE5EabdcAbd91571f', - '0xB12BFcA5A55806AaF64E99521918A4bf0fC40802', - ], - }, - '0x4988a896b1227218e4A686fdE5EabdcAbd91571f': { - decimals: 6, - symbol: 'USDT.e', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x07379565cD8B0CaE7c60Dc78e7f601b34AF2A21c', - '0xB12BFcA5A55806AaF64E99521918A4bf0fC40802', - ], - }, - '0xB12BFcA5A55806AaF64E99521918A4bf0fC40802': { - decimals: 6, - symbol: 'USDC.e', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [ - '0x07379565cD8B0CaE7c60Dc78e7f601b34AF2A21c', - '0x4988a896b1227218e4A686fdE5EabdcAbd91571f', - ], - }, - '0xd80d8688b02B3FD3afb81cDb124F188BB5aD0445': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - }, - '1666600000': { - '0x0b5740c6b4a97f90eF2F0220651Cca420B868FfB': { - decimals: 18, - symbol: 'nETH', - origin: ['nETH'], - destination: ['nETH'], - swappable: [], - }, - '0x1852F70512298d56e9c8FDd905e02581E04ddb2a': { - decimals: 18, - symbol: 'synFRAX', - origin: ['synFRAX'], - destination: ['synFRAX'], - swappable: [], - }, - '0x28b42698Caf46B4B012CF38b6C75867E0762186D': { - decimals: 18, - symbol: 'synJEWEL', - origin: ['JEWEL'], - destination: ['JEWEL'], - swappable: ['0x72Cb10C6bfA5624dD07Ef608027E366bd690048F'], - }, - '0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00': { - decimals: 18, - symbol: 'gOHM', - origin: ['gOHM'], - destination: ['gOHM'], - swappable: [], - }, - '0x72Cb10C6bfA5624dD07Ef608027E366bd690048F': { - decimals: 18, - symbol: 'JEWEL', - origin: ['JEWEL'], - destination: ['JEWEL'], - swappable: ['0x28b42698Caf46B4B012CF38b6C75867E0762186D'], - }, - '0xA9cE83507D872C5e1273E745aBcfDa849DAA654F': { - decimals: 18, - symbol: 'xJEWEL', - origin: ['xJEWEL'], - destination: ['xJEWEL'], - swappable: [], - }, - '0xD9eAA386cCD65F30b77FF175F6b52115FE454fD6': { - decimals: 18, - symbol: 'AVAX', - origin: ['AVAX'], - destination: ['AVAX'], - swappable: ['0xb12c13e66AdE1F72f71834f2FC5082Db8C091358'], - }, - '0xE3c82A836Ec85311a433fBd9486EfAF4b1AFbF48': { - decimals: 18, - symbol: 'SDT', - origin: ['SDT'], - destination: ['SDT'], - swappable: [], - }, - '0xE55e19Fb4F2D85af758950957714292DAC1e25B2': { - decimals: 18, - symbol: 'SYN', - origin: ['SYN'], - destination: ['SYN'], - swappable: [], - }, - '0xED2a7edd7413021d440b09D654f3b87712abAB66': { - decimals: 18, - symbol: 'nUSD', - origin: ['nUSD'], - destination: ['nUSD'], - swappable: [], - }, - '0xb12c13e66AdE1F72f71834f2FC5082Db8C091358': { - decimals: 18, - symbol: 'AVAX', - origin: ['AVAX'], - destination: ['AVAX'], - swappable: ['0xD9eAA386cCD65F30b77FF175F6b52115FE454fD6'], - }, - }, -} diff --git a/packages/synapse-constants/index.ts b/packages/synapse-constants/index.ts deleted file mode 100644 index 917d50c157..0000000000 --- a/packages/synapse-constants/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './constants/tokens/index' -export * as CHAINS from './constants/chains/index' -export * from './constants/types/index' -export * from './constants/assets/chains/index' -export * from './constants/assets/explorer/index' -export * from './constants/assets/icons/index' diff --git a/packages/synapse-constants/package.json b/packages/synapse-constants/package.json index f1f4789ef2..ee4e46c105 100644 --- a/packages/synapse-constants/package.json +++ b/packages/synapse-constants/package.json @@ -1,14 +1,19 @@ { "name": "synapse-constants", - "version": "1.3.24", + "version": "1.5.6", "description": "This is an npm package that maintains all synapse constants", - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "typings": "dist/index.d.ts", + "main": "dist/cjs/index.js", + "module": "dist/esm/index.js", + "types": "dist/types/index.d.ts", + "license": "MIT", "files": [ "dist" ], + "repository": { + "type": "git", + "url": "git+https://github.com/synapsecns/sanguine.git" + }, + "homepage": "https://github.com/synapsecns/sanguine/blob/master/packages/synapse-constants/README.md", "scripts": { "test:coverage": "echo 'No tests defined.'", "ci:lint": " ", @@ -16,34 +21,30 @@ "build:slither": " ", "lint": " ", "lint:fix": "npm run lint -- --fix", - "lint:check": "eslint . --max-warnings=0", - "build": "node scripts/generateMaps.js && node scripts/findMissing.js", - "prepublish": "tsc", - "compile": "tsc && copyfiles -u 1 \"constants/assets/**/*.*\" dist/constants && webpack", - "maps:generate": "yarn build && yarn compile" + "lint:check": "eslint . --max-warnings=0 --config .eslintrc.cjs", + "prepare": "rollup -c --bundleConfigAsCjs", + "build": "rollup -c --bundleConfigAsCjs", + "prepublish": "yarn build", + "maps:generate": "node ./src/scripts/generateMaps.cjs && node ./src/scripts/findMissing.cjs && yarn build" }, - "author": "", - "license": "ISC", "dependencies": { - "@codecov/webpack-plugin": "^0.0.1-beta.10", - "copyfiles": "^2.4.1", + "@ethersproject/address": "5.7.0", + "@ethersproject/bignumber": "5.7.0", "ethers": "5.7.2", - "lodash": "^4.17.21" + "lodash": "^4.17.21", + "viem": "^2.21.6" }, "devDependencies": { - "babel-loader": "^9.1.3", + "@codecov/rollup-plugin": "^1.2.0", + "@rollup/plugin-commonjs": "^28.0.0", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^15.3.0", + "@rollup/plugin-terser": "^0.4.4", + "@rollup/plugin-url": "^8.0.2", "babel-plugin-transform-export-extensions": "^6.22.0", - "file-loader": "^6.2.0", - "image-minimizer-webpack-plugin": "^3.8.3", - "imagemin": "^8.0.1", - "imagemin-jpegtran": "^7.0.0", - "imagemin-optipng": "^8.0.0", - "imagemin-svgo": "^10.0.1", + "rollup": "^4.22.4", + "rollup-plugin-typescript2": "^0.36.0", "svg-inline-loader": "^0.8.2", - "ts-loader": "^9.5.1", - "typescript": "^5.3.3", - "url-loader": "^4.1.1", - "webpack": "^5.89.0", - "webpack-cli": "^5.1.4" + "typescript": "^5.3.3" } } diff --git a/packages/synapse-constants/rollup.config.js b/packages/synapse-constants/rollup.config.js new file mode 100644 index 0000000000..45444b7f2c --- /dev/null +++ b/packages/synapse-constants/rollup.config.js @@ -0,0 +1,53 @@ +import typescript from 'rollup-plugin-typescript2' +import { nodeResolve } from '@rollup/plugin-node-resolve' +import commonjs from '@rollup/plugin-commonjs' +import json from '@rollup/plugin-json' +import terser from '@rollup/plugin-terser' +import url from '@rollup/plugin-url' +import { codecovRollupPlugin } from '@codecov/rollup-plugin' + +import packageJson from './package.json' + +export default [ + { + input: 'src/index.ts', + output: [ + { + file: packageJson.main, + format: 'cjs', + }, + { + file: packageJson.module, + format: 'esm', + }, + ], + plugins: [ + nodeResolve({ + preferBuiltins: true, + }), + commonjs(), + json(), + typescript({ + tsconfig: './tsconfig.json', + declaration: true, + declarationDir: './dist/types', + useTsconfigDeclarationDir: true, + }), + terser(), + url({ + include: ['**/*.svg', '**/*.png', '**/*.jpg'], + limit: 20000, + emitFiles: false, + }), + codecovRollupPlugin({ + enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, + bundleName: 'synapse-constants', + uploadToken: process.env.CODECOV_TOKEN, + uploadOverrides: { + sha: process.env.GH_COMMIT_SHA, + }, + }), + ], + external: ['lodash', 'ethers'], + }, +] diff --git a/packages/synapse-constants/constants/bridgeMap.ts b/packages/synapse-constants/src/constants/bridgeMap.ts similarity index 100% rename from packages/synapse-constants/constants/bridgeMap.ts rename to packages/synapse-constants/src/constants/bridgeMap.ts diff --git a/packages/synapse-constants/constants/chains/index.ts b/packages/synapse-constants/src/constants/chains/index.ts similarity index 99% rename from packages/synapse-constants/constants/chains/index.ts rename to packages/synapse-constants/src/constants/chains/index.ts index d58ce89949..9fa24989d2 100644 --- a/packages/synapse-constants/constants/chains/index.ts +++ b/packages/synapse-constants/src/constants/chains/index.ts @@ -1,4 +1,4 @@ -import { Chain } from '../types' +import { Chain } from '../../types' import * as all from './master' export * from './master' diff --git a/packages/synapse-constants/constants/chains/master.ts b/packages/synapse-constants/src/constants/chains/master.ts similarity index 69% rename from packages/synapse-constants/constants/chains/master.ts rename to packages/synapse-constants/src/constants/chains/master.ts index 41fd997bbb..580fac3f09 100644 --- a/packages/synapse-constants/constants/chains/master.ts +++ b/packages/synapse-constants/src/constants/chains/master.ts @@ -1,48 +1,4 @@ -import arbitrumImg from '../assets/chains/arbitrum.svg' -import auroraImg from '../assets/chains/aurora.svg' -import avalancheImg from '../assets/chains/avalanche.svg' -import baseImg from '../assets/chains/base.svg' -import bobaImg from '../assets/chains/boba.svg' -import bscImg from '../assets/chains/bnb.svg' -import blastImg from '../assets/chains/blast.svg' -import cantoImg from '../assets/chains/canto.svg' -import cronosImg from '../assets/chains/cronos.svg' -import dfkImg from '../assets/chains/dfk.svg' -import dogechainImg from '../assets/chains/dogechain.svg' -import ethImg from '../assets/chains/ethereum.svg' -import fantomImg from '../assets/chains/fantom.svg' -import harmonyImg from '../assets/chains/harmony.svg' -import klaytnImg from '../assets/chains/klaytn.svg' -import metisImg from '../assets/chains/metis.svg' -import moonbeamImg from '../assets/chains/moonbeam.svg' -import moonriverImg from '../assets/chains/moonriver.svg' -import optimismImg from '../assets/chains/optimism.svg' -import polygonImg from '../assets/chains/polygon.svg' -import scrollImg from '../assets/chains/scroll.svg' -import lineaImg from '../assets/chains/linea.svg' -import ethExplorerImg from '../assets/explorer/etherscan.svg' -import arbitrumExplorerImg from '../assets/explorer/arbitrum.svg' -import bnbExplorerImg from '../assets/explorer/bscscan.svg' -import avalancheExplorerImg from '../assets/explorer/avalanche.svg' -import cantoExplorerImg from '../assets/explorer/canto.svg' -import optimismExplorerImg from '../assets/explorer/optimism.svg' -import polygonExplorerImg from '../assets/explorer/polygon.svg' -import dfkExplorerImg from '../assets/explorer/dfk-chain.svg' -import klaytynExplorerImg from '../assets/explorer/klaytn.svg' -import fantomExplorerImg from '../assets/explorer/fantom.svg' -import cronosExplorerImg from '../assets/explorer/cronos.svg' -import bobaExplorerImg from '../assets/explorer/boba.svg' -import metisExplorerImg from '../assets/explorer/metis.svg' -import auroraExplorerImg from '../assets/explorer/aurora.svg' -import harmonyExplorerImg from '../assets/explorer/harmony.svg' -import moonbeamExplorerImg from '../assets/explorer/moonbeam.svg' -import moonriverExplorerImg from '../assets/explorer/moonriver.svg' -import dogeExplorerImg from '../assets/explorer/dogechain.svg' -import baseExplorerImg from '../assets/explorer/basescan.svg' -import blastExplorerImg from '../assets/explorer/blast.svg' -import scrollExplorerImg from '../assets/explorer/scroll.svg' -import lineaExplorerImg from '../assets/explorer/linea.svg' -import { Chain } from '../types' +import { Chain } from '../../types' export const ETH: Chain = { priorityRank: 100, @@ -50,7 +6,8 @@ export const ETH: Chain = { chainSymbol: 'ETH', name: 'Ethereum', codeName: 'ethereum', - chainImg: ethImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/ethereum.4a372106.svg', layer: 1, rpcUrls: { primary: 'https://rpc.ankr.com/eth', @@ -58,7 +15,8 @@ export const ETH: Chain = { }, explorerUrl: 'https://etherscan.io', explorerName: 'Etherscan', - explorerImg: ethExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/ethereum.4a372106.svg', blockTime: 12000, nativeCurrency: { name: 'Ethereum', @@ -73,7 +31,8 @@ export const ARBITRUM: Chain = { id: 42161, chainSymbol: 'ARBITRUM', name: 'Arbitrum', - chainImg: arbitrumImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/arbitrum.8ddb1b22.svg', layer: 2, codeName: 'arbitrum', blockTime: 300, @@ -84,7 +43,8 @@ export const ARBITRUM: Chain = { nativeCurrency: { name: 'Ethereum', symbol: 'ETH', decimals: 18 }, explorerUrl: 'https://arbiscan.io', explorerName: 'Arbiscan', - explorerImg: arbitrumExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/arbitrum.8ddb1b22.svg', color: 'gray', } @@ -93,7 +53,8 @@ export const BNB: Chain = { id: 56, chainSymbol: 'BNB', name: 'BNB Chain', - chainImg: bscImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/bnb.5948fe5e.svg', altName: 'BNB', layer: 1, codeName: 'bsc', @@ -105,7 +66,8 @@ export const BNB: Chain = { nativeCurrency: { name: 'Binance Coin', symbol: 'BNB', decimals: 18 }, explorerUrl: 'https://bscscan.com', explorerName: 'BscScan', - explorerImg: bnbExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/bscscan.a50e7cfb.svg', color: 'yellow', } @@ -114,7 +76,8 @@ export const AVALANCHE: Chain = { id: 43114, chainSymbol: 'AVALANCHE', name: 'Avalanche', - chainImg: avalancheImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/avalanche.9d53cbf0.svg', layer: 1, codeName: 'avalanche', blockTime: 2000, @@ -125,7 +88,8 @@ export const AVALANCHE: Chain = { nativeCurrency: { name: 'Avax', symbol: 'AVAX', decimals: 18 }, explorerUrl: 'https://snowscan.xyz/', explorerName: 'SnowScan', - explorerImg: avalancheExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/snowscan.1d03dfbf.svg', color: 'red', } @@ -134,7 +98,8 @@ export const CANTO: Chain = { id: 7700, chainSymbol: 'CANTO', name: 'Canto', - chainImg: cantoImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/canto.cb85e14f.svg', layer: 1, codeName: 'canto', blockTime: 6000, @@ -145,7 +110,8 @@ export const CANTO: Chain = { nativeCurrency: { name: 'Canto', symbol: 'CANTO', decimals: 18 }, explorerUrl: 'https://tuber.build/', explorerName: 'Canto Explorer', - explorerImg: cantoExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/snowscan.1d03dfbf.svg', color: 'green', } @@ -154,7 +120,8 @@ export const OPTIMISM: Chain = { id: 10, chainSymbol: 'OPTIMISM', name: 'Optimism', - chainImg: optimismImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/optimism.84d4f0ef.svg', layer: 2, codeName: 'optimism', blockTime: 2000, @@ -165,7 +132,8 @@ export const OPTIMISM: Chain = { nativeCurrency: { name: 'Ethereum', symbol: 'ETH', decimals: 18 }, explorerUrl: 'https://optimistic.etherscan.io', explorerName: 'Optimism Explorer', - explorerImg: optimismExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/optimism.84d4f0ef.svg', color: 'red', } @@ -174,7 +142,8 @@ export const POLYGON: Chain = { id: 137, chainSymbol: 'POLYGON', name: 'Polygon', - chainImg: polygonImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/polygon.237cd2b6.svg', layer: 2, codeName: 'polygon', blockTime: 2000, @@ -185,7 +154,8 @@ export const POLYGON: Chain = { nativeCurrency: { name: 'Matic', symbol: 'MATIC', decimals: 18 }, explorerUrl: 'https://polygonscan.com', explorerName: 'PolygonScan', - explorerImg: polygonExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/polygon.237cd2b6.svg', color: 'purple', } @@ -194,7 +164,8 @@ export const DFK: Chain = { id: 53935, chainSymbol: 'DFK', name: 'DFK Chain', - chainImg: dfkImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/dfk.2bd1f0e4.svg', layer: 1, codeName: 'dfk', blockTime: 2000, @@ -205,7 +176,8 @@ export const DFK: Chain = { nativeCurrency: { name: 'Jewel', symbol: 'JEWEL', decimals: 18 }, explorerUrl: 'https://subnets.avax.network/defi-kingdoms', explorerName: 'DFK Subnet Explorer', - explorerImg: dfkExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/dfk.2bd1f0e4.svg', color: 'lime', } @@ -214,7 +186,8 @@ export const KLAYTN: Chain = { id: 8217, chainSymbol: 'KLAYTN', name: 'Klaytn', - chainImg: klaytnImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/klaytn.59495fbb.svg', layer: 1, codeName: 'klaytn', blockTime: 1000, @@ -225,7 +198,8 @@ export const KLAYTN: Chain = { nativeCurrency: { name: 'Klaytn', symbol: 'KLAY', decimals: 18 }, explorerUrl: 'https://scope.klaytn.com', explorerName: 'Klaytn Explorer', - explorerImg: klaytynExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/klaytn.59495fbb.svg', color: 'orange', } @@ -234,7 +208,8 @@ export const FANTOM: Chain = { id: 250, chainSymbol: 'FANTOM', name: 'Fantom', - chainImg: fantomImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/fantom.1e444dad.svg', layer: 1, codeName: 'fantom', blockTime: 1000, @@ -245,7 +220,8 @@ export const FANTOM: Chain = { nativeCurrency: { name: 'Fantom', symbol: 'FTM', decimals: 18 }, explorerUrl: 'https://ftmscan.com', explorerName: 'FTMScan', - explorerImg: fantomExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/fantom.1e444dad.svg', color: 'blue', } @@ -254,7 +230,8 @@ export const CRONOS: Chain = { id: 25, chainSymbol: 'CRONOS', name: 'Cronos', - chainImg: cronosImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/cronos.b06f8311.svg', layer: 1, codeName: 'cronos', blockTime: 6000, @@ -265,7 +242,8 @@ export const CRONOS: Chain = { nativeCurrency: { name: 'Cronos', symbol: 'CRO', decimals: 18 }, explorerUrl: 'https://cronoscan.com', explorerName: 'CronoScan', - explorerImg: cronosExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/cronos.b06f8311.svg', color: 'gray', } @@ -274,7 +252,8 @@ export const BOBA: Chain = { id: 288, chainSymbol: 'BOBA', name: 'Boba Chain', - chainImg: bobaImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/boba.2072e50b.svg', layer: 2, codeName: 'boba', blockTime: 1000, @@ -285,7 +264,8 @@ export const BOBA: Chain = { nativeCurrency: { name: 'Ethereum', symbol: 'ETH', decimals: 18 }, explorerUrl: 'https://bobascan.com', explorerName: 'Boba Explorer', - explorerImg: bobaExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/boba.2072e50b.svg', color: 'lime', } @@ -294,7 +274,8 @@ export const METIS: Chain = { id: 1088, chainSymbol: 'METIS', name: 'Metis', - chainImg: metisImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/metis.90b6abf0.svg', layer: 2, codeName: 'metis', blockTime: 4000, @@ -305,7 +286,8 @@ export const METIS: Chain = { nativeCurrency: { name: 'Metis', symbol: 'METIS', decimals: 18 }, explorerUrl: 'https://andromeda-explorer.metis.io', explorerName: 'Metis Explorer', - explorerImg: metisExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/metis.90b6abf0.svg', color: 'teal', } @@ -314,7 +296,8 @@ export const AURORA: Chain = { id: 1313161554, chainSymbol: 'AURORA', name: 'Aurora', - chainImg: auroraImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/aurora.5a46037d.svg', layer: 1, codeName: 'aurora', blockTime: 1000, @@ -325,7 +308,8 @@ export const AURORA: Chain = { nativeCurrency: { name: 'Ethereum', symbol: 'ETH', decimals: 18 }, explorerUrl: 'https://explorer.mainnet.aurora.dev', explorerName: 'Aurora Explorer', - explorerImg: auroraExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/aurora.5a46037d.svg', color: 'lime', } @@ -334,7 +318,8 @@ export const HARMONY: Chain = { id: 1666600000, chainSymbol: 'HARMONY', name: 'Harmony', - chainImg: harmonyImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/harmony.af12f77e.svg', layer: 1, codeName: 'harmony', blockTime: 2000, @@ -345,7 +330,8 @@ export const HARMONY: Chain = { nativeCurrency: { name: 'Harmony One', symbol: 'ONE', decimals: 18 }, explorerUrl: 'https://explorer.harmony.one', explorerName: 'Harmony Explorer', - explorerImg: harmonyExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/harmony.af12f77e.svg', color: 'cyan', } @@ -354,7 +340,8 @@ export const MOONBEAM: Chain = { id: 1284, chainSymbol: 'MOONBEAM', name: 'Moonbeam', - chainImg: moonbeamImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/moonbeam.284ab9b4.svg', layer: 1, codeName: 'moonbeam', blockTime: 12000, @@ -365,7 +352,8 @@ export const MOONBEAM: Chain = { nativeCurrency: { name: 'Glimmer', symbol: 'GLMR', decimals: 18 }, explorerUrl: 'https://moonbeam.moonscan.io', explorerName: 'Moonbeam Explorer', - explorerImg: moonbeamExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/moonbeam.284ab9b4.svg', color: 'teal', } @@ -374,7 +362,8 @@ export const MOONRIVER: Chain = { id: 1285, chainSymbol: 'MOONRIVER', name: 'Moonriver', - chainImg: moonriverImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/moonriver.3fb35010.svg', layer: 1, codeName: 'moonriver', blockTime: 12000, @@ -385,7 +374,8 @@ export const MOONRIVER: Chain = { nativeCurrency: { name: 'Moonriver', symbol: 'MOVR', decimals: 18 }, explorerUrl: 'https://moonriver.moonscan.io', explorerName: 'Moonriver Explorer', - explorerImg: moonriverExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/moonriver.3fb35010.svg', color: 'purple', } @@ -394,7 +384,8 @@ export const DOGE: Chain = { id: 2000, chainSymbol: 'DOGE', name: 'Dogechain', - chainImg: dogechainImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/dogechain.36935650.svg', layer: 1, codeName: 'dogechain', blockTime: 2000, @@ -405,7 +396,8 @@ export const DOGE: Chain = { nativeCurrency: { name: 'DOGE', symbol: 'DOGE', decimals: 18 }, explorerUrl: 'https://explorer.dogechain.dog', explorerName: 'Dogechain Explorer', - explorerImg: dogeExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/dogechain.36935650.svg', color: 'purple', } @@ -415,7 +407,8 @@ export const BASE: Chain = { chainSymbol: 'ETH', name: 'Base', codeName: 'base', - chainImg: baseImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/base.d919fbef.svg', layer: 2, rpcUrls: { primary: 'https://base.blockpi.network/v1/rpc/public', @@ -423,7 +416,8 @@ export const BASE: Chain = { }, explorerUrl: 'https://basescan.org', explorerName: 'BaseScan', - explorerImg: baseExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/base.d919fbef.svg', blockTime: 3000, nativeCurrency: { name: 'Ether', @@ -439,7 +433,8 @@ export const BLAST: Chain = { chainSymbol: 'ETH', name: 'Blast', codeName: 'blast', - chainImg: blastImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/blast.e39807f8.svg', layer: 2, rpcUrls: { primary: 'https://rpc.blast.io', @@ -447,7 +442,8 @@ export const BLAST: Chain = { }, explorerUrl: 'https://blastscan.io', explorerName: 'Blastscan', - explorerImg: blastExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/blast.e39807f8.svg', blockTime: 3000, nativeCurrency: { name: 'Ether', @@ -463,7 +459,8 @@ export const SCROLL: Chain = { chainSymbol: 'SCROLL', name: 'Scroll', codeName: 'scroll', - chainImg: scrollImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/scroll.a805c122.svg', layer: 2, rpcUrls: { primary: 'https://rpc.scroll.io', @@ -471,7 +468,8 @@ export const SCROLL: Chain = { }, explorerUrl: 'https://scrollscan.com/', explorerName: 'Scrollscan', - explorerImg: scrollExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/scroll.a805c122.svg', blockTime: 3000, nativeCurrency: { name: 'Ether', @@ -487,7 +485,8 @@ export const LINEA: Chain = { chainSymbol: 'LINEA', name: 'Linea', codeName: 'linea', - chainImg: lineaImg, + chainImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/linea.e476f2ad.svg', layer: 2, rpcUrls: { primary: 'https://linea.blockpi.network/v1/rpc/public', @@ -495,7 +494,8 @@ export const LINEA: Chain = { }, explorerUrl: 'https://lineascan.build/', explorerName: 'LineaScan', - explorerImg: lineaExplorerImg, + explorerImg: + 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/scroll.a805c122.svg', blockTime: 3000, nativeCurrency: { name: 'Ether', diff --git a/packages/synapse-constants/constants/index.ts b/packages/synapse-constants/src/constants/index.ts similarity index 58% rename from packages/synapse-constants/constants/index.ts rename to packages/synapse-constants/src/constants/index.ts index 1f6865e221..4ad4121153 100644 --- a/packages/synapse-constants/constants/index.ts +++ b/packages/synapse-constants/src/constants/index.ts @@ -1,4 +1,3 @@ export * as TOKENS from './tokens' export * as CHAINS from './chains' -export * from './types' -export * from './assets' +export * from '../types' diff --git a/packages/synapse-constants/constants/minichef.ts b/packages/synapse-constants/src/constants/minichef.ts similarity index 100% rename from packages/synapse-constants/constants/minichef.ts rename to packages/synapse-constants/src/constants/minichef.ts diff --git a/packages/synapse-constants/constants/tokens/auxilliary.ts b/packages/synapse-constants/src/constants/tokens/auxilliary.ts similarity index 71% rename from packages/synapse-constants/constants/tokens/auxilliary.ts rename to packages/synapse-constants/src/constants/tokens/auxilliary.ts index dfc7407607..fe9d3c4a6b 100644 --- a/packages/synapse-constants/constants/tokens/auxilliary.ts +++ b/packages/synapse-constants/src/constants/tokens/auxilliary.ts @@ -1,10 +1,4 @@ -import avaxLogo from '../assets/icons/avax.svg' -import avwethLogo from '../assets/icons/avweth.svg' -import ethLogo from '../assets/icons/eth.svg' -import mimLogo from '../assets/icons/mim.svg' -import usdcLogo from '../assets/icons/usdc.svg' -import usdtLogo from '../assets/icons/usdt.svg' -import { Token } from '../types' +import { Token } from '../../types' import * as CHAINS from '../chains/master' export const AVWETH = new Token({ @@ -14,7 +8,7 @@ export const AVWETH = new Token({ decimals: 18, symbol: 'AVWETH', name: 'Aave Wrapped ETH', - logo: avwethLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/weth.19fa93ab.svg', swapableType: 'ETH', color: 'cyan', priorityRank: 2, @@ -30,7 +24,7 @@ export const KLAYTN_oUSDT = new Token({ }, symbol: 'orbitUSDT', name: 'Orbit Bridged USDT', - logo: usdtLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdt.3c9cd2f8.svg', swapableType: 'KLAYTN_USDT', swapableOn: [CHAINS.KLAYTN.id], priorityRank: 6, @@ -45,7 +39,7 @@ export const MIM = new Token({ decimals: 18, symbol: 'MIM', name: 'Magic Internet Money', - logo: mimLogo, + logo: 'https://docs.abracadabra.money/~gitbook/image?url=https%3A%2F%2F2388475231-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-Mc9U0yE30Tc9xb3mVGA%252F-McF-MUtKQnjF8iBPrv5%252F-McF8_XaAL4kyHrICXUw%252FMIM%2520Logo%2520PNG.png%3Falt%3Dmedia%26token%3D12b05d37-765f-494d-809b-4deab52bd212&width=768&dpr=2&quality=100&sign=7d2ede98&sv=1', swapableType: 'USD', color: 'indigo', priorityRank: 6, @@ -59,7 +53,7 @@ export const MULTIAVAX = new Token({ decimals: 18, symbol: 'multiAVAX', name: 'AnySwap Wrapped AVAX', - logo: avaxLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/avax.9d53cbf0.svg', swapableType: 'AVAX', swapableOn: [CHAINS.HARMONY.id], color: 'red', @@ -77,7 +71,7 @@ export const FANTOMUSDC = new Token({ }, symbol: 'USDC', name: 'USD Coin', - logo: usdcLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdc.d5dcb030.svg', swapableType: 'USD', swapableOn: [], color: 'blue', @@ -95,7 +89,7 @@ export const FANTOMUSDT = new Token({ }, symbol: 'USDT', name: 'USD Tether', - logo: usdtLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdt.3c9cd2f8.svg', color: 'lime', swapableType: 'USD', swapableOn: [], @@ -111,7 +105,7 @@ export const FANTOMETH = new Token({ decimals: 18, symbol: 'ETH', name: 'Ethereum', - logo: ethLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/eth.b3692688.svg', isNative: true, swapableType: 'ETH', color: 'sky', diff --git a/packages/synapse-constants/constants/tokens/bridgeable.ts b/packages/synapse-constants/src/constants/tokens/bridgeable.ts similarity index 86% rename from packages/synapse-constants/constants/tokens/bridgeable.ts rename to packages/synapse-constants/src/constants/tokens/bridgeable.ts index 3641b8a8aa..9f160fcd3a 100644 --- a/packages/synapse-constants/constants/tokens/bridgeable.ts +++ b/packages/synapse-constants/src/constants/tokens/bridgeable.ts @@ -1,45 +1,4 @@ -import ageurLogo from '../assets/icons/ageur.svg' -import avaxLogo from '../assets/icons/avax.svg' -import btcLogo from '../assets/icons/btc.svg' -import busdLogo from '../assets/icons/busd.svg' -import crvusdLogo from '../assets/icons/crvusd.svg' -import linkLogo from '../assets/icons/link.svg' -import daiLogo from '../assets/icons/dai.svg' -import dogLogo from '../assets/icons/dog.svg' -import ethLogo from '../assets/icons/eth.svg' -import fraxLogo from '../assets/icons/frax.svg' -import ftmLogo from '../assets/icons/ftm.svg' -import gmxLogo from '../assets/icons/gmx.svg' -import h2oLogo from '../assets/icons/h2o.svg' -import highLogo from '../assets/icons/highstreet.svg' -import hyperjumpLogo from '../assets/icons/hyperjump.svg' -import jewelLogo from '../assets/icons/jewel.png' -import klayLogo from '../assets/icons/klay.svg' -import l2daoLogo from '../assets/icons/l2dao.svg' -import maticLogo from '../assets/icons/matic.svg' -import movrLogo from '../assets/icons/movr.svg' -import nethLogo from '../assets/icons/neth.svg' -import newoLogo from '../assets/icons/newo.svg' -import nfdLogo from '../assets/icons/nfd.svg' -import noteLogo from '../assets/icons/note.svg' -import nusdLogo from '../assets/icons/nusd.svg' -import ohmLogo from '../assets/icons/ohm.svg' -import pepeLogo from '../assets/icons/pepe.svg' -import plsLogo from '../assets/icons/pls.svg' -import sdtLogo from '../assets/icons/sdt.svg' -import sfiLogo from '../assets/icons/sfi.svg' -import solarbeamLogo from '../assets/icons/solar.svg' -import susdLogo from '../assets/icons/susd.svg' -import synapseLogo from '../assets/icons/syn.svg' -import spectralLogo from '../assets/icons/spectral.svg' -import unidexLogo from '../assets/icons/unidex.svg' -import usdcLogo from '../assets/icons/usdc.svg' -import usdtLogo from '../assets/icons/usdt.svg' -import vstaLogo from '../assets/icons/vsta.svg' -import wbtcLogo from '../assets/icons/wbtc.svg' -import wethLogo from '../assets/icons/weth.svg' -import usdbLogo from '../assets/icons/usdb.svg' -import { Token } from '../types' +import { Token } from '../../types' import * as CHAINS from '../chains/master' const zeroAddress = '0x0000000000000000000000000000000000000000' @@ -64,7 +23,7 @@ export const GOHM = new Token({ decimals: 18, symbol: 'gOHM', name: 'Olympus DAO', - logo: ohmLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/ohm.1b779b45.svg', swapableType: 'OHM', color: 'gray', visibilityRank: 40, @@ -81,7 +40,7 @@ export const LINK = new Token({ decimals: 18, symbol: 'LINK', name: 'ChainLink Token', - logo: linkLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/link.bdd944c9.svg', swapableType: 'LINK', color: 'blue', priorityRank: 6, @@ -97,7 +56,7 @@ export const HIGH = new Token({ decimals: 18, symbol: 'HIGH', name: 'Highstreet', - logo: highLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/highstreet.e8060cc0.svg', swapableType: 'HIGH', color: 'cyan', priorityRank: 6, @@ -114,7 +73,7 @@ export const JUMP = new Token({ decimals: 18, symbol: 'JUMP', name: 'HyperJump', - logo: hyperjumpLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/hyperjump.e48ce883.svg', docUrl: '', swapableType: 'JUMP', color: 'cyan', @@ -130,7 +89,7 @@ export const SFI = new Token({ decimals: 18, symbol: 'SFI', name: 'Saffron Finance', - logo: sfiLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/sfi.4e35fbac.svg', docUrl: '', swapableType: 'SFI', color: 'red', @@ -148,7 +107,7 @@ export const DOG = new Token({ decimals: 18, symbol: 'DOG', name: 'The Doge NFT', - logo: dogLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/dog.b3513f9b.svg', docUrl: '', swapableType: 'DOG', color: 'yellow', @@ -167,7 +126,7 @@ export const NFD = new Token({ decimals: 18, symbol: 'NFD', name: 'Feisty Doge', - logo: nfdLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/nfd.1212b18d.svg', docUrl: '', swapableType: 'NFD', color: 'yellow', @@ -184,7 +143,7 @@ export const SOLAR = new Token({ decimals: 18, symbol: 'veSOLAR', name: 'Vested SolarBeam', - logo: solarbeamLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/solar.add1885e.svg', docUrl: '', swapableType: 'SOLAR', color: 'orange', @@ -203,7 +162,7 @@ export const GMX = new Token({ decimals: 18, symbol: 'GMX', name: 'GMX', - logo: gmxLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/gmx.0e372310.svg', docUrl: '', swapableType: 'GMX', priorityRank: 6, @@ -223,7 +182,7 @@ export const SDT = new Token({ decimals: 18, symbol: 'SDT', name: 'Stake DAO', - logo: sdtLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/sdt.c37b0b67.svg', docUrl: '', swapableType: 'SDT', color: 'gray', @@ -241,7 +200,7 @@ export const NEWO = new Token({ decimals: 18, symbol: 'NEWO', name: 'New Order', - logo: newoLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/newo.3c9ae85e.svg', docUrl: '', swapableType: 'NEWO', color: 'yellow', @@ -258,7 +217,7 @@ export const PEPE = new Token({ decimals: 18, symbol: 'PEPE', name: 'Pepe', - logo: pepeLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/pepe.5da81140.svg', swapableType: 'PEPE', priorityRank: 6, routeSymbol: 'PEPE', @@ -274,7 +233,7 @@ export const VSTA = new Token({ decimals: 18, symbol: 'VSTA', name: 'Vesta', - logo: vstaLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/vsta.56aa9f41.svg', docUrl: '', swapableType: 'VSTA', color: 'gray', @@ -297,7 +256,7 @@ export const H2O = new Token({ decimals: 18, symbol: 'H2O', name: 'H2O', - logo: h2oLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/h2o.108c0951.svg', docUrl: '', swapableType: 'H2O', color: 'cyan', @@ -314,7 +273,7 @@ export const L2DAO = new Token({ decimals: 18, symbol: 'L2DAO', name: 'Layer2DAO', - logo: l2daoLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/l2dao.0d1dd411.svg', docUrl: '', swapableType: 'L2DAO', color: 'cyan', @@ -331,7 +290,7 @@ export const PLS = new Token({ decimals: 18, symbol: 'PLS', name: 'Plutus', - logo: plsLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/pls.a97f40f2.svg', docUrl: '', swapableType: 'PLS', color: 'green', @@ -349,7 +308,7 @@ export const AGEUR = new Token({ decimals: 18, symbol: 'agEUR', name: 'Angle Euro', - logo: ageurLogo, + logo: 'https://45a97b3d.sanguine-fe.pages.dev/_next/static/media/ageur.588833db.svg', docUrl: '', swapableType: 'AGEUR', color: 'yellow', @@ -369,7 +328,7 @@ export const UNIDX = new Token({ decimals: 18, symbol: 'UNIDX', name: 'Unidex', - logo: unidexLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/unidex.29d556df.svg', docUrl: '', swapableType: 'UNIDX', color: 'gray', @@ -386,7 +345,7 @@ export const BUSD = new Token({ decimals: 18, symbol: 'BUSD', name: 'Binance USD', - logo: busdLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/busd.e8bb5032.svg', swapableType: 'BUSD', swapableOn: [CHAINS.BNB.id], color: 'yellow', @@ -437,7 +396,7 @@ export const USDC = new Token({ }, symbol: 'USDC', name: 'USD Coin', - logo: usdcLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdc.d5dcb030.svg', swapableType: 'USD', swapableOn: [ CHAINS.BNB.id, @@ -469,7 +428,7 @@ export const METISUSDC = new Token({ }, symbol: 'm.USDC', name: 'Metis USD Coin', - logo: usdcLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdc.d5dcb030.svg', swapableType: 'USD', swapableOn: [CHAINS.METIS.id], color: 'blue', @@ -510,7 +469,7 @@ export const USDT = new Token({ }, symbol: 'USDT', name: 'USD Tether', - logo: usdtLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdt.3c9cd2f8.svg', color: 'lime', swapableType: 'USD', swapableOn: [ @@ -548,7 +507,7 @@ export const DAI = new Token({ decimals: 18, symbol: 'DAI', name: 'Dai', - logo: daiLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/dai.ba6d3142.svg', swapableType: 'USD', swapableOn: [ CHAINS.ETH.id, @@ -577,7 +536,7 @@ export const WBTC = new Token({ }, symbol: 'WBTC', name: 'Wrapped BTC', - logo: wbtcLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/wbtc.d5fa58cc.svg', swapableType: 'WBTC', color: 'orange', priorityRank: 3, @@ -592,7 +551,7 @@ export const WETHE = new Token({ decimals: 18, symbol: 'WETH.e', name: 'Wrapped ETH', - logo: wethLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/weth.19fa93ab.svg', swapableType: 'ETH', swapableOn: [CHAINS.AVALANCHE.id], color: 'sky', @@ -607,7 +566,7 @@ export const ONEETH = new Token({ decimals: 18, symbol: '1ETH', name: 'Harmony ETH', - logo: wethLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/weth.19fa93ab.svg', swapableType: 'ETH', swapableOn: [CHAINS.HARMONY.id], color: 'sky', @@ -639,7 +598,7 @@ export const SYN = new Token({ decimals: 18, symbol: 'SYN', name: 'Synapse', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', swapableType: 'SYN', color: 'purple', visibilityRank: 90, @@ -658,7 +617,7 @@ export const FRAX = new Token({ decimals: 18, symbol: 'FRAX', name: 'Frax', - logo: fraxLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/frax.0f0670da.svg', swapableType: 'FRAX', color: 'gray', priorityRank: 6, @@ -676,7 +635,7 @@ export const SYNFRAX = new Token({ decimals: 18, symbol: 'synFRAX', name: 'Synapse Frax', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', swapableType: 'FRAX', color: 'gray', priorityRank: 4, @@ -703,7 +662,7 @@ export const NUSD = new Token({ decimals: 18, symbol: 'nUSD', name: 'Synapse nUSD', - logo: nusdLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/nusd.c7a22fdb.svg', swapableType: 'USD', swapableOn: [ CHAINS.BNB.id, @@ -733,7 +692,7 @@ export const NOTE = new Token({ decimals: 18, symbol: 'NOTE', name: 'Canto Note', - logo: noteLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/note.d5246fbd.svg', swapableType: 'USD', swapableOn: [CHAINS.CANTO.id], color: 'green', @@ -759,7 +718,7 @@ export const NETH = new Token({ decimals: 18, symbol: 'nETH', name: 'Synapse nETH', - logo: nethLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/neth.7c854d0f.svg', swapableType: 'ETH', swapableOn: [ CHAINS.OPTIMISM.id, @@ -794,7 +753,7 @@ export const ETH = new Token({ decimals: 18, symbol: 'ETH', name: 'Ethereum', - logo: ethLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/eth.b3692688.svg', isNative: true, swapableType: 'ETH', color: 'sky', @@ -819,7 +778,7 @@ export const MOVR = new Token({ decimals: 18, symbol: 'MOVR', name: 'MOVR', - logo: movrLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/movr.3fb35010.svg', isNative: true, swapableType: 'MOVR', color: 'purple', @@ -839,7 +798,7 @@ export const AVAX = new Token({ decimals: 18, symbol: 'AVAX', name: 'AVAX', - logo: avaxLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/avax.9d53cbf0.svg', isNative: true, swapableType: 'AVAX', color: 'red', @@ -856,7 +815,7 @@ export const WMOVR = new Token({ decimals: 18, symbol: 'MOVR', name: 'Wrapped MOVR', - logo: movrLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/movr.3fb35010.svg', swapableType: 'MOVR', color: 'purple', priorityRank: 3, @@ -870,7 +829,7 @@ export const WAVAX = new Token({ decimals: 18, symbol: 'AVAX', name: 'Wrapped AVAX', - logo: avaxLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/avax.9d53cbf0.svg', swapableType: 'AVAX', color: 'red', visibilityRank: 90, @@ -890,7 +849,7 @@ export const JEWEL = new Token({ decimals: 18, symbol: 'JEWEL', name: 'JEWEL', - logo: jewelLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/jewel.b2e41862.svg', color: 'lime', isNative: true, swapableType: 'JEWEL', @@ -906,7 +865,7 @@ export const WJEWEL = new Token({ decimals: 18, symbol: 'WJEWEL', name: 'Wrapped JEWEL', - logo: jewelLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/jewel.b2e41862.svg', swapableType: 'JEWEL', swapableOn: [CHAINS.HARMONY.id], color: 'lime', @@ -921,7 +880,7 @@ export const SYNJEWEL = new Token({ decimals: 18, symbol: 'synJEWEL', name: 'synJEWEL', - logo: jewelLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/jewel.b2e41862.svg', swapableType: 'JEWEL', swapableOn: [CHAINS.HARMONY.id], color: 'lime', @@ -937,7 +896,7 @@ export const XJEWEL = new Token({ decimals: 18, symbol: 'xJEWEL', name: 'xJEWEL', - logo: jewelLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/jewel.b2e41862.svg', swapableType: 'XJEWEL', color: 'lime', priorityRank: 3, @@ -956,7 +915,7 @@ export const USDCe = new Token({ decimals: 6, symbol: 'USDC.e', name: 'Bridged USDC', - logo: usdcLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdc.d5dcb030.svg', swapableType: 'USD', color: 'blue', swapableOn: [ @@ -978,7 +937,7 @@ export const USDTe = new Token({ decimals: 6, symbol: 'USDT.e', name: 'Bridged USDT', - logo: usdtLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdt.3c9cd2f8.svg', swapableType: 'USD', swapableOn: [CHAINS.AVALANCHE.id], visibilityRank: 100, @@ -996,7 +955,7 @@ export const SUSD = new Token({ }, symbol: 'sUSD', name: 'Synth sUSD', - logo: susdLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/susd.3aa0b6c4.svg', color: 'purple', swapableType: 'USD', swapableOn: [CHAINS.OPTIMISM.id], @@ -1019,7 +978,7 @@ export const WSOHM = new Token({ }, symbol: 'wsOHM', name: 'Wrapped sOHM', - logo: ohmLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/ohm.1b779b45.svg', color: 'gray', swapableType: 'OHM', visibilityRank: 40, @@ -1035,7 +994,7 @@ export const ONEDAI = new Token({ decimals: 18, symbol: '1DAI', name: 'Harmony Dai Stablecoin', - logo: daiLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/dai.ba6d3142.svg', swapableType: 'USD', swapableOn: [CHAINS.HARMONY.id], color: 'yellow', @@ -1054,7 +1013,7 @@ export const ONEUSDC = new Token({ }, symbol: '1USDC', name: 'Harmony USD Coin', - logo: usdcLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdc.d5dcb030.svg', swapableType: 'USD', swapableOn: [CHAINS.HARMONY.id], color: 'blue', @@ -1071,7 +1030,7 @@ export const ONEUSDT = new Token({ }, symbol: '1USDT', name: 'Harmony USD Tether', - logo: usdtLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdt.3c9cd2f8.svg', color: 'lime', swapableType: 'USD', swapableOn: [CHAINS.HARMONY.id], @@ -1093,7 +1052,7 @@ export const BTCB = new Token({ }, symbol: 'BTC.b', name: 'Bitcoin', - logo: btcLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/btc.63077225.svg', swapableType: 'BTC.b', color: 'orange', priorityRank: 3, @@ -1108,7 +1067,7 @@ export const DAIE = new Token({ decimals: 18, symbol: 'DAI.e', name: 'Dai.e Token', - logo: daiLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/dai.ba6d3142.svg', swapableType: 'USD', swapableOn: [CHAINS.AVALANCHE.id], color: 'yellow', @@ -1128,7 +1087,7 @@ export const KLAY = new Token({ }, symbol: 'KLAY', name: 'Klaytn', - logo: klayLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/klay.a50be928.svg', isNative: true, swapableType: 'KLAY', color: 'red', @@ -1146,7 +1105,7 @@ export const WKLAY = new Token({ }, symbol: 'WKLAY', name: 'Wrapped Klaytn', - logo: klayLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/klay.a50be928.svg', swapableType: 'WKLAY', color: 'red', priorityRank: 3, @@ -1161,7 +1120,7 @@ export const MATIC = new Token({ decimals: 18, symbol: 'MATIC', name: 'MATIC', - logo: maticLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/matic.237cd2b6.svg', isNative: true, swapableType: 'MATIC', color: 'blue', @@ -1178,7 +1137,7 @@ export const WMATIC = new Token({ decimals: 18, symbol: 'WMATIC', name: 'WMATIC', - logo: maticLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/matic.237cd2b6.svg', swapableType: 'MATIC', color: 'blue', visibilityRank: 90, @@ -1194,7 +1153,7 @@ export const FTM = new Token({ decimals: 18, symbol: 'FTM', name: 'Fantom', - logo: ftmLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/ftm.af71a88f.svg', swapableType: 'FTM', swapableOn: [CHAINS.FANTOM.id], color: 'blue', @@ -1210,7 +1169,7 @@ export const WFTM = new Token({ decimals: 18, symbol: 'WFTM', name: 'Wrapped Fantom', - logo: ftmLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/ftm.af71a88f.svg', swapableType: 'FTM', swapableOn: [CHAINS.FANTOM.id], color: 'blue', @@ -1234,7 +1193,7 @@ export const WETH = new Token({ decimals: 18, symbol: 'WETH', name: 'Wrapped ETH', - logo: wethLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/weth.19fa93ab.svg', swapableType: 'ETH', color: 'sky', priorityRank: 3, @@ -1252,7 +1211,7 @@ export const CRVUSDC = new Token({ swapExceptions: {}, symbol: 'crvUSD', name: 'Curve.fi USD', - logo: crvusdLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/crvusd.e65e6479.svg', swapableType: 'USD', swapableOn: [], color: 'yellow', @@ -1272,7 +1231,7 @@ export const USDBC = new Token({ swapExceptions: {}, symbol: 'USDbC', name: 'USD Base Coin', - logo: usdcLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdc.d5dcb030.svg', swapableType: 'USD', swapableOn: [], color: 'blue', @@ -1292,7 +1251,7 @@ export const USDB = new Token({ swapExceptions: {}, symbol: 'USDB', name: 'Blast Rebasing USD', - logo: usdbLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdb.33190604.svg', swapableType: 'USD', swapableOn: [], color: 'blue', @@ -1312,7 +1271,7 @@ export const SPECTRAL = new Token({ }, symbol: 'SPEC', name: 'Spectral Token', - logo: spectralLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/spectral.6d51750c.svg', swapableType: 'SPEC', color: 'blue', routeSymbol: 'SPEC', diff --git a/packages/synapse-constants/constants/tokens/deprecated.ts b/packages/synapse-constants/src/constants/tokens/deprecated.ts similarity index 79% rename from packages/synapse-constants/constants/tokens/deprecated.ts rename to packages/synapse-constants/src/constants/tokens/deprecated.ts index 6ff49c7566..6d76314adf 100644 --- a/packages/synapse-constants/constants/tokens/deprecated.ts +++ b/packages/synapse-constants/src/constants/tokens/deprecated.ts @@ -1,6 +1,4 @@ -import usdbLogo from '../assets/icons/usdc.svg' -import fusdtLogo from '../assets/icons/usdt.svg' -import { Token } from '../types' +import { Token } from '../../types' import * as CHAINS from '../chains/master' export const USDB = new Token({ @@ -10,7 +8,7 @@ export const USDB = new Token({ decimals: 18, symbol: 'USDB', name: 'USDB', - logo: usdbLogo, + logo: 'https://bscscan.com/token/images/usdb_32.png', docUrl: '', swapableType: 'USDB', priorityRank: 6, @@ -26,7 +24,7 @@ export const FUSDT = new Token({ }, symbol: 'fUSDT', name: 'Frapped USDT', - logo: fusdtLogo, + logo: 'https://ftmscan.com/token/images/frappedusdt_32.png', color: 'lime', swapableType: 'USD', swapableOn: [CHAINS.FANTOM.id], diff --git a/packages/synapse-constants/constants/tokens/index.ts b/packages/synapse-constants/src/constants/tokens/index.ts similarity index 98% rename from packages/synapse-constants/constants/tokens/index.ts rename to packages/synapse-constants/src/constants/tokens/index.ts index 7b8326afd9..531e27c61b 100644 --- a/packages/synapse-constants/constants/tokens/index.ts +++ b/packages/synapse-constants/src/constants/tokens/index.ts @@ -1,6 +1,6 @@ import _ from 'lodash' -import { Token } from '../types/index' +import { Token } from '../../types/index' import * as CHAINS from '../chains/master' import * as all from './bridgeable' import * as allPool from './poolMaster' @@ -51,7 +51,7 @@ export const findChainIdsWithPausedToken = (routeSymbol: string) => { PAUSED_TOKENS_BY_CHAIN, (result, tokens, chainId) => { if (_.includes(tokens, routeSymbol)) { - result.push(chainId) + result.push(chainId as never) } return result }, diff --git a/packages/synapse-constants/constants/tokens/master.ts b/packages/synapse-constants/src/constants/tokens/master.ts similarity index 100% rename from packages/synapse-constants/constants/tokens/master.ts rename to packages/synapse-constants/src/constants/tokens/master.ts diff --git a/packages/synapse-constants/constants/tokens/poolMaster.ts b/packages/synapse-constants/src/constants/tokens/poolMaster.ts similarity index 90% rename from packages/synapse-constants/constants/tokens/poolMaster.ts rename to packages/synapse-constants/src/constants/tokens/poolMaster.ts index 972d8711e6..d096ed5fba 100644 --- a/packages/synapse-constants/constants/tokens/poolMaster.ts +++ b/packages/synapse-constants/src/constants/tokens/poolMaster.ts @@ -1,6 +1,4 @@ -import synapseLogo from '../assets/icons/syn.svg' -import { Token } from '../types' - +import { Token } from '../../types' import * as CHAINS from '../chains/master' import { BUSD, @@ -45,7 +43,7 @@ export const ETH_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD', name: 'Synapse nUSD LP Token Ethereum', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Ethereum Stableswap Pool', routerIndex: 'eth3pool', poolId: 420, @@ -72,7 +70,7 @@ export const BSC_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'BSC Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'bscnusd', poolId: 1, @@ -99,7 +97,7 @@ export const OPTIMISM_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token Optimism ', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Optimism Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'optimismnusd', poolId: 1, @@ -124,7 +122,7 @@ export const CRONOS_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token Cronos ', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Cronos Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'cronosnusd', poolId: 0, @@ -149,7 +147,7 @@ export const POLYGON_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token Polygon ', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Polygon Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'polygonnusd', poolId: 1, @@ -176,7 +174,7 @@ export const AVALANCHE_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token Avalanche', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Avalanche Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'avalanchenusd', // poolId: 3, @@ -202,7 +200,7 @@ export const LEGACY_AVALANCHE_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token Avalanche', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Legacy Avalanche Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'legacyavalanchenusd', poolId: 1, @@ -225,7 +223,7 @@ export const ARBITRUM_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token Arbitrum', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Legacy Arbitrum Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'arbitrumnusd', poolId: 2, @@ -252,7 +250,7 @@ export const FANTOM_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token Fantom', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Legacy Fantom Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'fantomnusd', poolId: 1, @@ -279,7 +277,7 @@ export const HARMONY_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token Harmony', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Harmony Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'harmonynusd', poolId: 1, @@ -306,7 +304,7 @@ export const BOBA_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token Boba', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Boba Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'bobanusd', poolId: 1, @@ -333,7 +331,7 @@ export const AURORA_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Synapse nUSD LP Token Aurora', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Legacy Aurora Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'auroranusd', poolId: 0, @@ -358,7 +356,7 @@ export const AURORA_TS_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSD-LP', name: 'Trisolaris nUSD LP Token Aurora', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Aurora Trisolaris Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'auroratrisolarisnusd', poolId: 0, @@ -384,7 +382,7 @@ export const ARBITRUM_3POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSDLP', name: 'Synapse nUSD LP Token Arbitrum', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Arbitrum 3Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'arbitrum3pool', poolId: 3, @@ -410,7 +408,7 @@ export const FANTOM_3POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSDLP', name: 'Synapse nUSD LP Token Fantom', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Fantom 3Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'fantom3pool', poolId: 3, @@ -436,7 +434,7 @@ export const METIS_POOL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nUSDLP', name: 'Synapse nUSD LP Token Metis', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Metis Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'metis2pool', poolId: 0, @@ -462,7 +460,7 @@ export const CANTO_POOL_SWAP_TOKEN = new Token({ decimals: 18, name: 'Synapse nUSD LP Token Canto', symbol: 'nUSDLP', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Canto NOTE Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'canto2pool', poolId: 0, @@ -486,7 +484,7 @@ export const CANTO_POOL_SWAP_TOKEN = new Token({ // decimals: 18, // name: 'Synapse nUSD LP Token Canto', // symbol: 'nUSD-LP', -// logo: synapseLogo, +// logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', // poolName: 'Canto USDC Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL // routerIndex: 'cantousdcpool', // poolId: 2, @@ -509,7 +507,7 @@ export const CANTO_WRAPPER_POOL_SWAP_TOKEN = new Token({ decimals: 18, name: 'Synapse nUSD LP Token Canto', symbol: 'Wrapper', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Canto Wrapper Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'cantowrapper', poolId: 420, @@ -535,7 +533,7 @@ export const KLAYTN_ORBIT_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'oUSDT-LP', name: 'Synapse Orbit UST LP Token Klaytn', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Klaytn Synapse & Orbit USDT Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'klaytn2pool', poolId: 0, @@ -560,7 +558,7 @@ export const HARMONY_AVAX_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'AVAXLP', name: 'AVAX LP Token Harmony ', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Harmony AVAX Swap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'avax2pool', poolId: 0, @@ -587,7 +585,7 @@ export const ARBITRUM_ETH_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nETH-LP', // make sure this gets update to match conytract name: 'Synapse Eth LP Token Arbitrum', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Arbitrum ETH Pool', routerIndex: 'arbitrumethpool', poolId: 0, @@ -618,7 +616,7 @@ export const OPTIMISM_ETH_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nETH-LP', // make sure this gets update to match conytract name: 'Synapse Eth LP Token Optimism', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Optimism ETH Pool', routerIndex: 'optimismethpool', poolId: 0, @@ -649,7 +647,7 @@ export const BOBA_ETH_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nETH-LP', // make sure this gets update to match conytract name: 'Synapse Eth LP Token Boba', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Boba ETH Pool', routerIndex: 'bobaethpool', poolId: 2, @@ -680,7 +678,7 @@ export const AVALANCHE_AVETH_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nETH-LP', // make sure this gets update to match conytract name: 'Synapse Eth LP Token Avalanche', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Avalanche ETH Pool', routerIndex: 'avalancheethpool', poolId: 2, @@ -715,7 +713,7 @@ export const HARMONY_ONEETH_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nETH-LP', name: 'Synapse 1ETH LP Token Harmony', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Harmony 1ETH Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'harmonyethpool', poolId: 2, @@ -741,7 +739,7 @@ export const FANTOM_WETH_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nETH-LP', name: 'Synapse ETH LP Token Fantom', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Fantom ETH Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'fantomethpool', poolId: 2, @@ -767,7 +765,7 @@ export const METIS_WETH_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nETH-LP', name: 'Synapse ETH LP Token Metis', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Metis ETH Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'metisethpool', poolId: 1, @@ -794,7 +792,7 @@ export const CANTO_WETH_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nETH-LP', name: 'Synapse ETH LP Token Canto', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Canto ETH Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'cantoethpool', poolId: 1, @@ -820,7 +818,7 @@ export const HARMONY_JEWEL_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'JEWELP', name: 'Jewel LP Token Harmony ', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Harmony Jewel Swap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'jewel2pool', poolId: 0, @@ -845,7 +843,7 @@ export const BASE_ETH_SWAP_TOKEN = new Token({ decimals: 18, symbol: 'nETH-LP', name: 'Synapse Eth LP Token Base', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Base ETH Pool', routerIndex: 'baseethpool', poolId: 0, @@ -878,7 +876,7 @@ export const METIS_WETH_SWAP_TOKEN_MIGRATED = new Token({ decimals: 18, symbol: 'nETH-LP', name: 'Synapse ETH LP Token Metis', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Metis ETH Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'metisethpool-migrated', poolId: 1, @@ -904,7 +902,7 @@ export const METIS_POOL_SWAP_TOKEN_MIGRATED = new Token({ decimals: 18, symbol: 'nUSDLP', name: 'Synapse nUSD LP Token Metis', - logo: synapseLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'Metis Stableswap Pool ', // DONT GET RID OF SPACE AFTER POOL routerIndex: 'metis2pool-migrated', poolId: 0, diff --git a/packages/synapse-constants/constants/tokens/sushiMaster.ts b/packages/synapse-constants/src/constants/tokens/sushiMaster.ts similarity index 81% rename from packages/synapse-constants/constants/tokens/sushiMaster.ts rename to packages/synapse-constants/src/constants/tokens/sushiMaster.ts index ca302ef70f..347142245b 100644 --- a/packages/synapse-constants/constants/tokens/sushiMaster.ts +++ b/packages/synapse-constants/src/constants/tokens/sushiMaster.ts @@ -1,6 +1,4 @@ -import sushiLogo from '../assets/icons/sushi.svg' -import { Token } from '../types' - +import { Token } from '../../types' import * as CHAINS from '../chains/master' import { MINICHEF_ADDRESSES } from '../minichef' @@ -11,7 +9,7 @@ export const SYN_ETH_SUSHI_TOKEN = new Token({ decimals: 18, symbol: 'SYN/ETH-SLP', name: 'SYN/ETH Sushi LP', - logo: sushiLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/syn.042f8cd0.svg', poolName: 'SYN/ETH Sushiswap LP', poolId: 0, poolType: 'EXTERNAL_LP', @@ -28,7 +26,7 @@ export const ETH_USDC_SUSHI_TOKEN = new Token({ decimals: 18, symbol: 'ETH/USDC-SLP', name: 'ETH/USDC Sushi LP', - logo: sushiLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/eth.b3692688.svg', poolName: 'ETH/USDC Sushiswap LP', poolId: 666, poolType: 'EXTERNAL_LP', diff --git a/packages/synapse-constants/constants/tokens/swapMaster.ts b/packages/synapse-constants/src/constants/tokens/swapMaster.ts similarity index 84% rename from packages/synapse-constants/constants/tokens/swapMaster.ts rename to packages/synapse-constants/src/constants/tokens/swapMaster.ts index e5a94bc9dd..dea9ff5a28 100644 --- a/packages/synapse-constants/constants/tokens/swapMaster.ts +++ b/packages/synapse-constants/src/constants/tokens/swapMaster.ts @@ -1,6 +1,4 @@ -import { Token } from '../types' -import usdtLogo from '../assets/icons/usdt.svg' -import usdcLogo from '../assets/icons/usdc.svg' +import { Token } from '../../types' import * as CHAINS from '../chains/master' export const SwapUSDC = new Token({ @@ -13,7 +11,7 @@ export const SwapUSDC = new Token({ }, symbol: 'USDC', name: 'USD Circle', - logo: usdcLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdc.d5dcb030.svg', description: ` USD Coin (known by its ticker USDC) is a stablecoin that is pegged to the U.S. dollar on a 1:1 basis. Every unit of this cryptocurrency in circulation @@ -35,7 +33,7 @@ export const SwapUSDT = new Token({ }, symbol: 'USDT', name: 'USD Tether', - logo: usdtLogo, + logo: 'https://8f3ea9f2.sanguine-fe.pages.dev/_next/static/media/usdt.3c9cd2f8.svg', color: 'lime', description: ` USDT mirrors the price of the U.S. dollar, issued by a Hong Kong-based company Tether. diff --git a/packages/synapse-constants/custom.d.ts b/packages/synapse-constants/src/custom.d.ts similarity index 100% rename from packages/synapse-constants/custom.d.ts rename to packages/synapse-constants/src/custom.d.ts diff --git a/packages/synapse-constants/src/index.ts b/packages/synapse-constants/src/index.ts new file mode 100644 index 0000000000..24f061e0e5 --- /dev/null +++ b/packages/synapse-constants/src/index.ts @@ -0,0 +1,6 @@ +export * from './constants/tokens/index' +export * as CHAINS from './constants/chains/index' +export * from './types/index' + +export { MINICHEF_ADDRESSES } from './constants/minichef' +export { BRIDGE_MAP } from './constants/bridgeMap' diff --git a/packages/synapse-constants/scripts/abi/IDefaultPool.json b/packages/synapse-constants/src/scripts/abi/IDefaultPool.json similarity index 100% rename from packages/synapse-constants/scripts/abi/IDefaultPool.json rename to packages/synapse-constants/src/scripts/abi/IDefaultPool.json diff --git a/packages/synapse-constants/scripts/abi/IERC20Metadata.json b/packages/synapse-constants/src/scripts/abi/IERC20Metadata.json similarity index 100% rename from packages/synapse-constants/scripts/abi/IERC20Metadata.json rename to packages/synapse-constants/src/scripts/abi/IERC20Metadata.json diff --git a/packages/synapse-constants/scripts/abi/SwapQuoter.json b/packages/synapse-constants/src/scripts/abi/SwapQuoter.json similarity index 100% rename from packages/synapse-constants/scripts/abi/SwapQuoter.json rename to packages/synapse-constants/src/scripts/abi/SwapQuoter.json diff --git a/packages/synapse-constants/scripts/abi/SynapseCCTP.json b/packages/synapse-constants/src/scripts/abi/SynapseCCTP.json similarity index 100% rename from packages/synapse-constants/scripts/abi/SynapseCCTP.json rename to packages/synapse-constants/src/scripts/abi/SynapseCCTP.json diff --git a/packages/synapse-constants/scripts/abi/SynapseCCTPRouter.json b/packages/synapse-constants/src/scripts/abi/SynapseCCTPRouter.json similarity index 100% rename from packages/synapse-constants/scripts/abi/SynapseCCTPRouter.json rename to packages/synapse-constants/src/scripts/abi/SynapseCCTPRouter.json diff --git a/packages/synapse-constants/scripts/abi/SynapseRouter.json b/packages/synapse-constants/src/scripts/abi/SynapseRouter.json similarity index 100% rename from packages/synapse-constants/scripts/abi/SynapseRouter.json rename to packages/synapse-constants/src/scripts/abi/SynapseRouter.json diff --git a/packages/synapse-constants/scripts/data/ignoredBridgeSymbols.json b/packages/synapse-constants/src/scripts/data/ignoredBridgeSymbols.json similarity index 100% rename from packages/synapse-constants/scripts/data/ignoredBridgeSymbols.json rename to packages/synapse-constants/src/scripts/data/ignoredBridgeSymbols.json diff --git a/packages/synapse-constants/scripts/data/legacyTokens.json b/packages/synapse-constants/src/scripts/data/legacyTokens.json similarity index 100% rename from packages/synapse-constants/scripts/data/legacyTokens.json rename to packages/synapse-constants/src/scripts/data/legacyTokens.json diff --git a/packages/synapse-constants/scripts/data/providers.json b/packages/synapse-constants/src/scripts/data/providers.json similarity index 100% rename from packages/synapse-constants/scripts/data/providers.json rename to packages/synapse-constants/src/scripts/data/providers.json diff --git a/packages/synapse-constants/scripts/data/symbolOverrides.json b/packages/synapse-constants/src/scripts/data/symbolOverrides.json similarity index 100% rename from packages/synapse-constants/scripts/data/symbolOverrides.json rename to packages/synapse-constants/src/scripts/data/symbolOverrides.json diff --git a/packages/synapse-constants/scripts/findMissing.js b/packages/synapse-constants/src/scripts/findMissing.cjs similarity index 100% rename from packages/synapse-constants/scripts/findMissing.js rename to packages/synapse-constants/src/scripts/findMissing.cjs diff --git a/packages/synapse-constants/scripts/generateMaps.js b/packages/synapse-constants/src/scripts/generateMaps.cjs similarity index 99% rename from packages/synapse-constants/scripts/generateMaps.js rename to packages/synapse-constants/src/scripts/generateMaps.cjs index f37ada1328..93e9f41b13 100644 --- a/packages/synapse-constants/scripts/generateMaps.js +++ b/packages/synapse-constants/src/scripts/generateMaps.cjs @@ -1,7 +1,7 @@ const { ethers } = require('ethers') -const { prettyPrintTS } = require('./utils/prettyPrintTs') -const { fetchRfqData } = require('./utils/fetchRfqData') +const { prettyPrintTS } = require('./utils/prettyPrintTs.cjs') +const { fetchRfqData } = require('./utils/fetchRfqData.cjs') // Provider URLs const providers = require('./data/providers.json') // List of ignored bridge symbols diff --git a/packages/synapse-constants/src/scripts/output/unsupportedTokens.txt b/packages/synapse-constants/src/scripts/output/unsupportedTokens.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/synapse-constants/scripts/utils/fetchRfqData.js b/packages/synapse-constants/src/scripts/utils/fetchRfqData.cjs similarity index 100% rename from packages/synapse-constants/scripts/utils/fetchRfqData.js rename to packages/synapse-constants/src/scripts/utils/fetchRfqData.cjs diff --git a/packages/synapse-constants/scripts/utils/prettyPrintTs.js b/packages/synapse-constants/src/scripts/utils/prettyPrintTs.cjs similarity index 100% rename from packages/synapse-constants/scripts/utils/prettyPrintTs.js rename to packages/synapse-constants/src/scripts/utils/prettyPrintTs.cjs diff --git a/packages/synapse-constants/constants/types/index.tsx b/packages/synapse-constants/src/types/index.ts similarity index 92% rename from packages/synapse-constants/constants/types/index.tsx rename to packages/synapse-constants/src/types/index.ts index 45569b975c..f30e6c12f5 100644 --- a/packages/synapse-constants/constants/types/index.tsx +++ b/packages/synapse-constants/src/types/index.ts @@ -1,6 +1,7 @@ import { BigNumber } from '@ethersproject/bignumber' -import * as CHAINS from '../chains/master' -import { getAddress } from '@ethersproject/address' + +import { makeMultiChainObj } from '../utils/makeMultiChainObj' +import { validateAddresses } from '../utils/validateAddresses' export type Chain = { id: number @@ -19,6 +20,7 @@ export type Chain = { priorityRank?: number color?: string } + export type PoolToken = { symbol: string percent: string @@ -28,6 +30,7 @@ export type PoolToken = { isLP: boolean rawBalance: bigint } + export type Query = [string, string, BigNumber, BigNumber, string] & { swapAdapter: string tokenOut: string @@ -35,12 +38,14 @@ export type Query = [string, string, BigNumber, BigNumber, string] & { deadline: BigNumber rawParams: string } + export type PoolUserData = { name: string tokens: PoolToken[] lpTokenBalance: bigint nativeTokens?: any } + export type PoolData = { name: string tokens: PoolToken[] @@ -54,26 +59,31 @@ export type PoolData = { interface TokensByChain { [cID: string]: Token[] } + export type PoolCardInfo = { index: number label: string poolsByChain: TokensByChain } + export enum WalletId { MetaMask = 'metaMask', WalletConnect = 'walletConnect', CoinbaseWallet = 'coinbaseWallet', } + export interface IconProps { walletId?: string className?: string } + export type PoolTokenObject = { token: Token balance: string rawBalance: bigint isLP: boolean } + /** * Represents an ERC20-like token with a unique address, chainId, and some metadata. */ @@ -277,27 +287,3 @@ export class Token { this.routeSymbol = routeSymbol } } - -const makeMultiChainObj = (valOrObj: any) => { - if (typeof valOrObj === 'object') { - return valOrObj - } else { - const obj: { [key: number]: any } = {} - for (const chain of Object.values(CHAINS)) { - obj[chain.id] = valOrObj - } - return obj - } -} - -const validateAddresses = (addresses: { - [x: number]: string -}): { [x: number]: string } => { - const reformatted: { [x: number]: string } = {} - for (const chainId in addresses) { - reformatted[chainId] = addresses[chainId] - ? getAddress(addresses[chainId]) - : '' - } - return reformatted -} diff --git a/packages/synapse-constants/src/utils/makeMultiChainObj.ts b/packages/synapse-constants/src/utils/makeMultiChainObj.ts new file mode 100644 index 0000000000..18a66dcb2b --- /dev/null +++ b/packages/synapse-constants/src/utils/makeMultiChainObj.ts @@ -0,0 +1,13 @@ +import * as CHAINS from '../constants/chains/master' + +export const makeMultiChainObj = (valOrObj: any) => { + if (typeof valOrObj === 'object') { + return valOrObj + } else { + const obj: { [key: number]: any } = {} + for (const chain of Object.values(CHAINS)) { + obj[chain.id] = valOrObj + } + return obj + } +} diff --git a/packages/synapse-constants/src/utils/validateAddresses.ts b/packages/synapse-constants/src/utils/validateAddresses.ts new file mode 100644 index 0000000000..517cd7a7e4 --- /dev/null +++ b/packages/synapse-constants/src/utils/validateAddresses.ts @@ -0,0 +1,13 @@ +import { getAddress } from '@ethersproject/address' + +export const validateAddresses = (addresses: { + [x: number]: string +}): { [x: number]: string } => { + const reformatted: { [x: number]: string } = {} + for (const chainId in addresses) { + reformatted[chainId] = addresses[chainId] + ? getAddress(addresses[chainId]) + : '' + } + return reformatted +} diff --git a/packages/synapse-constants/tsconfig.json b/packages/synapse-constants/tsconfig.json index dc0b7533d7..5105bbc9a4 100644 --- a/packages/synapse-constants/tsconfig.json +++ b/packages/synapse-constants/tsconfig.json @@ -1,24 +1,15 @@ { "compilerOptions": { - "outDir": "./dist", - "allowJs": true, - "rootDir": "./", + "module": "ESNext", "target": "ES2019", - "module": "CommonJS", "moduleResolution": "node", + "declaration": true, + "declarationDir": "./dist/types", + "outDir": "./dist", + "strict": true, "esModuleInterop": true, - "declaration": false, - "jsx": "react", - "skipLibCheck": true + "resolveJsonModule": true }, - "include": [ - "./constants/**/*", - "./custom.d.ts", - "./index.ts", - "./index.d.ts" - ], - "exclude": [ - "node_modules", - "**/*.spec.ts" - ] -} \ No newline at end of file + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/synapse-constants/webpack.config.js b/packages/synapse-constants/webpack.config.js deleted file mode 100644 index 6ba4758070..0000000000 --- a/packages/synapse-constants/webpack.config.js +++ /dev/null @@ -1,86 +0,0 @@ -const path = require('path') - -const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin') -const { codecovWebpackPlugin } = require('@codecov/webpack-plugin') - -module.exports = { - mode: 'production', - - entry: './dist/', - - output: { - path: path.resolve(__dirname, 'dist'), - filename: 'bundle.js', - }, - plugins: [ - // Put the Codecov Webpack plugin after all other plugins - codecovWebpackPlugin({ - enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined, - bundleName: 'synapse-constants', - uploadToken: process.env.CODECOV_TOKEN, - uploadOverrides: { - sha: process.env.GH_COMMIT_SHA, - }, - }), - ], - - resolve: { - extensions: ['.ts', '.tsx', '.js'], - modules: [path.resolve(__dirname, '../../node_modules')], - }, - - module: { - rules: [ - { - test: /\.ts|tsx$/, - loader: 'ts-loader', - exclude: /node_modules/, - }, - { - test: /\.svg$/, - loader: 'svg-inline-loader', - }, - { - test: /\.(png|jpg|jpeg|gif)$/, - type: 'asset', - }, - ], - }, - optimization: { - minimizer: [ - '...', - new ImageMinimizerPlugin({ - minimizer: { - implementation: ImageMinimizerPlugin.imageminMinify, - options: { - // Lossless optimization with custom option - // Feel free to experiment with options for better result for you - plugins: [ - ['optipng', { optimizationLevel: 5 }], - // Svgo configuration here https://github.com/svg/svgo#configuration - [ - 'svgo', - { - plugins: [ - { - name: 'preset-default', - params: { - overrides: { - removeViewBox: false, - inlineStyles: { - onlyMatchedOnce: false, - }, - }, - }, - }, - ], - multipass: true, - }, - ], - ], - }, - }, - }), - ], - }, -} diff --git a/packages/synapse-interface/CHANGELOG.md b/packages/synapse-interface/CHANGELOG.md index 7e358764a8..4ebfa78640 100644 --- a/packages/synapse-interface/CHANGELOG.md +++ b/packages/synapse-interface/CHANGELOG.md @@ -3,6 +3,82 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.40.3](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.40.2...@synapsecns/synapse-interface@0.40.3) (2024-10-02) + +**Note:** Version bump only for package @synapsecns/synapse-interface + + + + + +## [0.40.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.40.1...@synapsecns/synapse-interface@0.40.2) (2024-10-01) + +**Note:** Version bump only for package @synapsecns/synapse-interface + + + + + +## [0.40.1](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.40.0...@synapsecns/synapse-interface@0.40.1) (2024-09-26) + + +### Bug Fixes + +* **synapse-interface:** Generalizes airdrop decimal display based on SDK gasAirdropAmount [SLT-269] ([#3196](https://github.com/synapsecns/sanguine/issues/3196)) ([aa37b50](https://github.com/synapsecns/sanguine/commit/aa37b503786824d7a2460640be7d2484f0491071)) + + + + + +# [0.40.0](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.39.2...@synapsecns/synapse-interface@0.40.0) (2024-09-26) + + +### Features + +* **synapse-interface:** refund RFQ transaction [SLT-272] ([#3197](https://github.com/synapsecns/sanguine/issues/3197)) ([f0b13bc](https://github.com/synapsecns/sanguine/commit/f0b13bc456620004a1787f62e87f404d95272356)) + + + + + +## [0.39.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.39.1...@synapsecns/synapse-interface@0.39.2) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/synapse-interface + + + + + +## [0.39.1](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.39.0...@synapsecns/synapse-interface@0.39.1) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/synapse-interface + + + + + +# [0.39.0](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.38.9...@synapsecns/synapse-interface@0.39.0) (2024-09-23) + + +### Features + +* **synapse-interface:** confirm new price [SLT-150] ([#3084](https://github.com/synapsecns/sanguine/issues/3084)) ([6f21b1a](https://github.com/synapsecns/sanguine/commit/6f21b1a7f6eb2ea3885582fcd678fa122f9f87e5)) + + + + + +## [0.38.9](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.38.8...@synapsecns/synapse-interface@0.38.9) (2024-09-23) + + +### Bug Fixes + +* **synapse-interface:** Additional checks on screen [SLT-166] ([#3152](https://github.com/synapsecns/sanguine/issues/3152)) ([9418b40](https://github.com/synapsecns/sanguine/commit/9418b40aa25a441d6a4460695962f7fbf41c4221)) + + + + + ## [0.38.8](https://github.com/synapsecns/sanguine/compare/@synapsecns/synapse-interface@0.38.7...@synapsecns/synapse-interface@0.38.8) (2024-09-20) **Note:** Version bump only for package @synapsecns/synapse-interface diff --git a/packages/synapse-interface/README.md b/packages/synapse-interface/README.md index 4023fc2c54..ca91c23a00 100644 --- a/packages/synapse-interface/README.md +++ b/packages/synapse-interface/README.md @@ -2,7 +2,7 @@ This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next ## Getting Started -First, run the development server: +First, run development server: ```bash yarn dev diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeExchangeRateInfo.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeExchangeRateInfo.tsx index 52c9e9ffcb..7b59d6e8df 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeExchangeRateInfo.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeExchangeRateInfo.tsx @@ -11,6 +11,7 @@ import { EMPTY_BRIDGE_QUOTE } from '@/constants/bridge' import { CHAINS_BY_ID } from '@constants/chains' import * as CHAINS from '@constants/chains/master' import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks' +import { getSignificantDecimals } from '@/utils/getSignificantDecimals' export const BridgeExchangeRateInfo = () => { /* TODO: @@ -134,7 +135,6 @@ const TimeEstimate = () => { } const GasDropLabel = () => { - let decimalsToDisplay const { toChainId } = useBridgeState() const { bridgeQuote: { gasDropAmount }, @@ -143,20 +143,13 @@ const GasDropLabel = () => { const t = useTranslations('Bridge') const symbol = CHAINS_BY_ID[toChainId]?.nativeCurrency.symbol - if ([CHAINS.FANTOM.id].includes(toChainId)) { - decimalsToDisplay = 2 - } else if ( - [CHAINS.BNB.id, CHAINS.AVALANCHE.id, CHAINS.BOBA.id].includes(toChainId) - ) { - decimalsToDisplay = 3 - } else { - decimalsToDisplay = 4 - } + const stringifiedGasAmount = formatBigIntToString(gasDropAmount, 18) + const significantDecimals = getSignificantDecimals(stringifiedGasAmount) const formattedGasDropAmount = formatBigIntToString( gasDropAmount, 18, - decimalsToDisplay + significantDecimals ) const airdropInDollars = getAirdropInDollars(symbol, formattedGasDropAmount) @@ -214,12 +207,13 @@ const getAirdropInDollars = ( symbol: string, formattedGasDropAmount: string ) => { + const decimals = symbol === 'JEWEL' ? 4 : 2 const price = useCoingeckoPrice(symbol) if (price) { const airdropInDollars = parseFloat(formattedGasDropAmount) * price - return airdropInDollars.toFixed(2) + return airdropInDollars.toFixed(decimals) } else { return undefined } diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeQuoteResetTimer.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeQuoteResetTimer.tsx new file mode 100644 index 0000000000..86717650ba --- /dev/null +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeQuoteResetTimer.tsx @@ -0,0 +1,113 @@ +import { useState, useEffect, useMemo } from 'react' + +import { BridgeQuote } from '@/utils/types' +import { convertMsToSeconds } from '@/utils/time' + +export const BridgeQuoteResetTimer = ({ + bridgeQuote, + isLoading, + isActive, + duration, // in ms +}: { + bridgeQuote: BridgeQuote + isLoading: boolean + isActive: boolean + duration: number +}) => { + const memoizedTimer = useMemo(() => { + if (!isActive) return null + + if (isLoading) { + return + } else { + return ( + + ) + } + }, [bridgeQuote, duration, isActive]) + + return memoizedTimer +} + +const AnimatedLoadingCircle = () => { + return ( + + + + + + ) +} + +const AnimatedProgressCircle = ({ + animateKey, + duration, +}: { + animateKey: string + duration: number +}) => { + const [animationKey, setAnimationKey] = useState(0) + + useEffect(() => { + setAnimationKey((prevKey) => prevKey + 1) + }, [animateKey]) + + return ( + + + + + + + + + + + + ) +} diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx index 44aa9a3fb5..86f6006650 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx @@ -12,6 +12,7 @@ import { useBridgeDisplayState, useBridgeState } from '@/slices/bridge/hooks' import { TransactionButton } from '@/components/buttons/TransactionButton' import { useBridgeValidations } from './hooks/useBridgeValidations' import { segmentAnalyticsEvent } from '@/contexts/SegmentAnalyticsProvider' +import { useConfirmNewBridgePrice } from './hooks/useConfirmNewBridgePrice' export const BridgeTransactionButton = ({ approveTxn, @@ -19,6 +20,7 @@ export const BridgeTransactionButton = ({ isApproved, isBridgePaused, isTyping, + isQuoteStale, }) => { const dispatch = useAppDispatch() const { openConnectModal } = useConnectModal() @@ -48,6 +50,8 @@ export const BridgeTransactionButton = ({ debouncedFromValue, } = useBridgeState() const { bridgeQuote, isLoading } = useBridgeQuoteState() + const { isPendingConfirmChange, onUserAcceptChange } = + useConfirmNewBridgePrice() const { isWalletPending } = useWalletState() const { showDestinationWarning, isDestinationWarningAccepted } = @@ -73,6 +77,7 @@ export const BridgeTransactionButton = ({ isBridgeQuoteAmountGreaterThanInputForRfq || (isConnected && !hasValidQuote) || (isConnected && !hasSufficientBalance) || + (isConnected && isQuoteStale) || (destinationAddress && !isAddress(destinationAddress)) let buttonProperties @@ -97,6 +102,26 @@ export const BridgeTransactionButton = ({ label: t('Please select an Origin token'), onClick: null, } + } else if (isConnected && !hasSufficientBalance) { + buttonProperties = { + label: t('Insufficient balance'), + onClick: null, + } + } else if (isLoading && hasValidQuote) { + buttonProperties = { + label: isPendingConfirmChange + ? t('Confirm new quote') + : t('Bridge {symbol}', { symbol: fromToken?.symbol }), + pendingLabel: t('Bridge {symbol}', { symbol: fromToken?.symbol }), + onClick: null, + className: ` + ${ + isPendingConfirmChange + ? '!outline !outline-1 !outline-synapsePurple !outline-offset-[-1px] !from-bgLight !to-bgLight' + : '!bg-gradient-to-r !from-fuchsia-500 !to-purple-500 dark:!to-purple-600' + } + !opacity-100`, + } } else if (isLoading) { buttonProperties = { label: t('Bridge {symbol}', { symbol: fromToken?.symbol }), @@ -144,11 +169,6 @@ export const BridgeTransactionButton = ({ label: t('Invalid bridge quote'), onClick: null, } - } else if (!isLoading && isConnected && !hasSufficientBalance) { - buttonProperties = { - label: t('Insufficient balance'), - onClick: null, - } } else if (destinationAddress && !isAddress(destinationAddress)) { buttonProperties = { label: t('Invalid Destination address'), @@ -167,6 +187,13 @@ export const BridgeTransactionButton = ({ onClick: () => switchChain({ chainId: fromChainId }), pendingLabel: t('Switching chains'), } + } else if (isApproved && hasValidQuote && isPendingConfirmChange) { + buttonProperties = { + label: t('Confirm new quote'), + onClick: () => onUserAcceptChange(), + className: + '!outline !outline-1 !outline-synapsePurple !outline-offset-[-1px] !from-bgLight !to-bgLight', + } } else if (!isApproved && hasValidInput && hasValidQuote) { buttonProperties = { onClick: approveTxn, diff --git a/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx b/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx index c891391fcd..5af733cb91 100644 --- a/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx @@ -17,7 +17,11 @@ import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks' import { useBridgeValidations } from './hooks/useBridgeValidations' import { useTranslations } from 'next-intl' -export const OutputContainer = () => { +interface OutputContainerProps { + isQuoteStale: boolean +} + +export const OutputContainer = ({ isQuoteStale }: OutputContainerProps) => { const { address } = useAccount() const { bridgeQuote, isLoading } = useBridgeQuoteState() const { showDestinationAddress } = useBridgeDisplayState() @@ -33,6 +37,8 @@ export const OutputContainer = () => { } }, [bridgeQuote, hasValidInput, hasValidQuote]) + const inputClassName = isQuoteStale ? 'opacity-50' : undefined + return (
@@ -48,6 +54,7 @@ export const OutputContainer = () => { disabled={true} showValue={showValue} isLoading={isLoading} + className={inputClassName} /> diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useBridgeValidations.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useBridgeValidations.ts index e64ac72587..b3f31ab0f6 100644 --- a/packages/synapse-interface/components/StateManagedBridge/hooks/useBridgeValidations.ts +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useBridgeValidations.ts @@ -111,7 +111,7 @@ export const useBridgeValidations = () => { } } -const constructStringifiedBridgeSelections = ( +export const constructStringifiedBridgeSelections = ( originAmount, originChainId, originToken, diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts new file mode 100644 index 0000000000..2dbead7f24 --- /dev/null +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useConfirmNewBridgePrice.ts @@ -0,0 +1,125 @@ +import { useState, useEffect, useMemo, useRef } from 'react' + +import { useBridgeState } from '@/slices/bridge/hooks' +import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks' +import { constructStringifiedBridgeSelections } from './useBridgeValidations' +import { BridgeQuote } from '@/utils/types' + +export const useConfirmNewBridgePrice = () => { + const triggerQuoteRef = useRef(null) + const bpsThreshold = 0.0001 // 1bps + + const [hasQuoteOutputChanged, setHasQuoteOutputChanged] = + useState(false) + const [hasUserConfirmedChange, setHasUserConfirmedChange] = + useState(false) + + const { bridgeQuote, previousBridgeQuote } = useBridgeQuoteState() + const { debouncedFromValue, fromToken, toToken, fromChainId, toChainId } = + useBridgeState() + + const currentBridgeQuoteSelections = useMemo( + () => + constructStringifiedBridgeSelections( + debouncedFromValue, + fromChainId, + fromToken, + toChainId, + toToken + ), + [debouncedFromValue, fromChainId, fromToken, toChainId, toToken] + ) + + const previousBridgeQuoteSelections = useMemo( + () => + constructStringifiedBridgeSelections( + previousBridgeQuote?.inputAmountForQuote, + previousBridgeQuote?.originChainId, + previousBridgeQuote?.originTokenForQuote, + previousBridgeQuote?.destChainId, + previousBridgeQuote?.destTokenForQuote + ), + [previousBridgeQuote] + ) + + const hasSameSelectionsAsPreviousQuote = useMemo( + () => currentBridgeQuoteSelections === previousBridgeQuoteSelections, + [currentBridgeQuoteSelections, previousBridgeQuoteSelections] + ) + + const isPendingConfirmChange = + hasQuoteOutputChanged && + hasSameSelectionsAsPreviousQuote && + !hasUserConfirmedChange + + useEffect(() => { + const validQuotes = + bridgeQuote?.outputAmount && previousBridgeQuote?.outputAmount + + const hasBridgeModuleChanged = + bridgeQuote?.bridgeModuleName !== + (triggerQuoteRef.current?.bridgeModuleName ?? + previousBridgeQuote?.bridgeModuleName) + + const outputAmountDiffMoreThanThreshold = validQuotes + ? calculateOutputRelativeDifference( + bridgeQuote, + triggerQuoteRef.current ?? previousBridgeQuote + ) > bpsThreshold + : false + + if ( + validQuotes && + hasSameSelectionsAsPreviousQuote && + (outputAmountDiffMoreThanThreshold || hasBridgeModuleChanged) + ) { + requestUserConfirmChange(previousBridgeQuote) + } else { + resetConfirm() + } + }, [bridgeQuote, previousBridgeQuote, hasSameSelectionsAsPreviousQuote]) + + const requestUserConfirmChange = (previousQuote: BridgeQuote) => { + if (!hasQuoteOutputChanged && !hasUserConfirmedChange) { + triggerQuoteRef.current = previousQuote + setHasQuoteOutputChanged(true) + } + setHasUserConfirmedChange(false) + } + + const resetConfirm = () => { + if (hasUserConfirmedChange) { + triggerQuoteRef.current = null + setHasQuoteOutputChanged(false) + setHasUserConfirmedChange(false) + } + } + + const onUserAcceptChange = () => { + triggerQuoteRef.current = null + setHasUserConfirmedChange(true) + } + + return { + isPendingConfirmChange, + onUserAcceptChange, + } +} + +const calculateOutputRelativeDifference = ( + currentQuote?: BridgeQuote, + previousQuote?: BridgeQuote +) => { + if (!currentQuote?.outputAmountString || !previousQuote?.outputAmountString) { + return null + } + + const currentOutput = parseFloat(currentQuote.outputAmountString) + const previousOutput = parseFloat(previousQuote.outputAmountString) + + if (previousOutput === 0) { + return null + } + + return (previousOutput - currentOutput) / previousOutput +} diff --git a/packages/synapse-interface/components/StateManagedBridge/hooks/useStaleQuoteUpdater.ts b/packages/synapse-interface/components/StateManagedBridge/hooks/useStaleQuoteUpdater.ts new file mode 100644 index 0000000000..9a9cbf1444 --- /dev/null +++ b/packages/synapse-interface/components/StateManagedBridge/hooks/useStaleQuoteUpdater.ts @@ -0,0 +1,114 @@ +import { useEffect, useRef, useState } from 'react' + +import { BridgeQuote } from '@/utils/types' +import { useIntervalTimer } from '@/utils/hooks/useIntervalTimer' + +export const useStaleQuoteUpdater = ( + quote: BridgeQuote, + refreshQuoteCallback: () => Promise, + enabled: boolean, + staleTimeout: number = 15000, // in ms + autoRefreshDuration: number = 30000 // in ms +) => { + const [isStale, setIsStale] = useState(false) + const autoRefreshIntervalRef = useRef(null) + const autoRefreshStartTimeRef = useRef(null) + const mouseMoveListenerRef = useRef void)>(null) + const manualRefreshRef = useRef(null) + + useIntervalTimer(staleTimeout, !enabled) + + const [mouseMoved, resetMouseMove] = useTrackMouseMove() + + const clearManualRefreshTimeout = () => { + if (manualRefreshRef.current) { + clearTimeout(manualRefreshRef.current) + } + } + + const clearAutoRefreshInterval = () => { + if (autoRefreshIntervalRef.current) { + clearInterval(autoRefreshIntervalRef.current) + } + } + + const clearMouseMoveListener = () => { + if (mouseMoveListenerRef.current) { + mouseMoveListenerRef.current = null + } + } + + useEffect(() => { + if (mouseMoved && autoRefreshStartTimeRef.current) { + autoRefreshStartTimeRef.current = null + resetMouseMove() + } + }, [quote]) + + // Start auto-refresh logic for ${autoRefreshDuration}ms seconds + useEffect(() => { + if (enabled) { + // If auto-refresh has not started yet, initialize the start time + if (autoRefreshStartTimeRef.current === null) { + autoRefreshStartTimeRef.current = Date.now() + } + + const elapsedTime = Date.now() - autoRefreshStartTimeRef.current + + // If ${autoRefreshDuration}ms hasn't passed, keep auto-refreshing + if (elapsedTime < autoRefreshDuration) { + clearManualRefreshTimeout() + clearAutoRefreshInterval() + + autoRefreshIntervalRef.current = setInterval(() => { + refreshQuoteCallback() + }, staleTimeout) + } else { + // If more than ${autoRefreshDuration}ms have passed, stop auto-refreshing and switch to mousemove logic + clearAutoRefreshInterval() + + manualRefreshRef.current = setTimeout(() => { + clearMouseMoveListener() + setIsStale(true) + + const handleMouseMove = () => { + refreshQuoteCallback() + clearMouseMoveListener() + setIsStale(false) + } + + document.addEventListener('mousemove', handleMouseMove, { + once: true, + }) + + mouseMoveListenerRef.current = handleMouseMove + }, staleTimeout) + } + } + + return () => { + clearManualRefreshTimeout() + clearAutoRefreshInterval() + setIsStale(false) + } + }, [quote, enabled]) + + return isStale +} + +export const useTrackMouseMove = (): [boolean, () => void] => { + const [moved, setMoved] = useState(false) + + const onMove = () => setMoved(true) + const onReset = () => setMoved(false) + + useEffect(() => { + document.addEventListener('mousemove', onMove) + + return () => { + document.removeEventListener('mousemove', onMove) + } + }, []) + + return [moved, onReset] +} diff --git a/packages/synapse-interface/components/_Transaction/_Transaction.tsx b/packages/synapse-interface/components/_Transaction/_Transaction.tsx index 1a1158825d..1aea90352b 100644 --- a/packages/synapse-interface/components/_Transaction/_Transaction.tsx +++ b/packages/synapse-interface/components/_Transaction/_Transaction.tsx @@ -19,6 +19,7 @@ import { TransactionSupport } from './components/TransactionSupport' import { RightArrow } from '@/components/icons/RightArrow' import { Address } from 'viem' import { useIsTxReverted } from './helpers/useIsTxReverted' +import { useTxRefundStatus } from './helpers/useTxRefundStatus' interface _TransactionProps { connectedAddress: string @@ -30,11 +31,12 @@ interface _TransactionProps { destinationToken: Token originTxHash: string bridgeModuleName: string + routerAddress: string estimatedTime: number // in seconds timestamp: number currentTime: number kappa?: string - status: 'pending' | 'completed' | 'reverted' + status: 'pending' | 'completed' | 'reverted' | 'refunded' disabled: boolean } @@ -49,6 +51,7 @@ export const _Transaction = ({ destinationToken, originTxHash, bridgeModuleName, + routerAddress, estimatedTime, timestamp, currentTime, @@ -80,6 +83,7 @@ export const _Transaction = ({ isEstimatedTimeReached, isCheckTxStatus, isCheckTxForRevert, + isCheckTxForRefund, } = calculateEstimatedTimeStatus(currentTime, timestamp, estimatedTime) const [isTxCompleted, _kappa] = useBridgeTxStatus({ @@ -98,18 +102,29 @@ export const _Transaction = ({ isCheckTxForRevert && status === 'pending' ) + const isTxRefunded = useTxRefundStatus( + kappa, + routerAddress as Address, + originChain, + isCheckTxForRefund && + status === 'pending' && + bridgeModuleName === 'SynapseRFQ' + ) + useBridgeTxUpdater( connectedAddress, destinationChain, _kappa, originTxHash, isTxCompleted, - isTxReverted + isTxReverted, + isTxRefunded ) // Show transaction support if the transaction is delayed by more than 5 minutes and not finalized or reverted const showTransactionSupport = status === 'reverted' || + status === 'refunded' || (status === 'pending' && delayedTimeInMin && delayedTimeInMin <= -5) return ( @@ -184,7 +199,7 @@ export const _Transaction = ({ {status !== 'pending' && ( { const currentTime = getUnixTimeMinutesBeforeNow(0) const elapsedTime = currentTime - startTime @@ -25,7 +25,7 @@ export const AnimatedProgressBar = memo( const percentElapsed = (elapsedTime / estDuration) * 100 const isComplete = status === 'completed' - const isError = status === 'reverted' + const isError = status === 'reverted' || status === 'refunded' let duration = isComplete ? 0.5 : remainingTime diff --git a/packages/synapse-interface/components/_Transaction/components/TimeRemaining.tsx b/packages/synapse-interface/components/_Transaction/components/TimeRemaining.tsx index fda528f15c..f7920c9473 100644 --- a/packages/synapse-interface/components/_Transaction/components/TimeRemaining.tsx +++ b/packages/synapse-interface/components/_Transaction/components/TimeRemaining.tsx @@ -12,7 +12,7 @@ export const TimeRemaining = ({ isDelayed: boolean remainingTime: number delayedTime: number | null - status: 'pending' | 'completed' | 'reverted' + status: 'pending' | 'completed' | 'reverted' | 'refunded' }) => { const t = useTranslations('Time') @@ -36,6 +36,14 @@ export const TimeRemaining = ({ ) } + if (status === 'refunded') { + return ( + + {t('Refunded')} + + ) + } + if (isDelayed) { const delayedTimeInMin = Math.floor(delayedTime / 60) const absoluteDelayedTime = Math.abs(delayedTimeInMin) diff --git a/packages/synapse-interface/components/_Transaction/components/TransactionSupport.tsx b/packages/synapse-interface/components/_Transaction/components/TransactionSupport.tsx index 2803d73911..02b09ab5fa 100644 --- a/packages/synapse-interface/components/_Transaction/components/TransactionSupport.tsx +++ b/packages/synapse-interface/components/_Transaction/components/TransactionSupport.tsx @@ -1,8 +1,11 @@ import { useTranslations } from 'next-intl' import { TRANSACTION_SUPPORT_URL, DISCORD_URL } from '@/constants/urls' -export const TransactionSupport = ({ status }: { status: string }) => { - const isReverted = status === 'reverted' +export const TransactionSupport = ({ + status, +}: { + status: 'pending' | 'completed' | 'reverted' | 'refunded' +}) => { const t = useTranslations('Time') return ( @@ -10,12 +13,16 @@ export const TransactionSupport = ({ status }: { status: string }) => { id="transaction-support" className="flex items-center justify-between w-full py-1 pl-3 pr-1 text-sm" > - {isReverted ? ( + {status === 'reverted' && (
{t('Transaction reverted, funds returned')}
- ) : ( -
{t("What's taking so long?")}
)} + {status === 'refunded' && ( +
{t('Transaction refunded, funds returned')}
+ )} + + {status === 'pending' &&
{t("What's taking so long?")}
} +
30 + const isCheckTxForRefund = elapsedTime > fourHoursInSeconds const delayedTime = isEstimatedTimeReached ? remainingTime : null const delayedTimeInMin = remainingTime ? Math.floor(remainingTime / 60) : null @@ -33,5 +35,6 @@ export const calculateEstimatedTimeStatus = ( isEstimatedTimeReached, isCheckTxStatus, isCheckTxForRevert, + isCheckTxForRefund, } } diff --git a/packages/synapse-interface/components/_Transaction/helpers/useBridgeTxUpdater.ts b/packages/synapse-interface/components/_Transaction/helpers/useBridgeTxUpdater.ts index f493893111..9466b9f670 100644 --- a/packages/synapse-interface/components/_Transaction/helpers/useBridgeTxUpdater.ts +++ b/packages/synapse-interface/components/_Transaction/helpers/useBridgeTxUpdater.ts @@ -5,6 +5,7 @@ import { updateTransactionKappa, completeTransaction, revertTransaction, + refundTransaction, _TransactionDetails, } from '@/slices/_transactions/reducer' import { fetchAndStoreSingleNetworkPortfolioBalances } from '@/slices/portfolio/hooks' @@ -27,7 +28,8 @@ export const useBridgeTxUpdater = ( kappa: string, originTxHash: string, isTxComplete: boolean, - isTxReverted: boolean + isTxReverted: boolean, + isTxRefunded: boolean ) => { const dispatch = useAppDispatch() const { transactions } = use_TransactionsState() @@ -49,6 +51,13 @@ export const useBridgeTxUpdater = ( } }, [isTxReverted]) + /** Update tx for refunds in store */ + useEffect(() => { + if (isTxRefunded && storedTx.status !== 'refunded') { + dispatch(refundTransaction({ originTxHash })) + } + }, [isTxRefunded]) + /** Update tx for completion in store */ useEffect(() => { if (isTxComplete && originTxHash && kappa) { diff --git a/packages/synapse-interface/components/_Transaction/helpers/useTxRefundStatus.ts b/packages/synapse-interface/components/_Transaction/helpers/useTxRefundStatus.ts new file mode 100644 index 0000000000..351f97f04e --- /dev/null +++ b/packages/synapse-interface/components/_Transaction/helpers/useTxRefundStatus.ts @@ -0,0 +1,103 @@ +import { type Address } from 'viem' +import { isNumber, isString } from 'lodash' +import { useEffect, useState } from 'react' +import { readContract } from '@wagmi/core' + +import { type Chain } from '@/utils/types' +import { useIntervalTimer } from '@/utils/hooks/useIntervalTimer' +import { wagmiConfig } from '@/wagmiConfig' +import fastBridgeAbi from '@/constants/abis/fastBridge.json' +import fastBridgeRouterAbi from '@/constants/abis/fastBridgeRouter.json' + +enum BridgeStatus { + NULL, + REQUESTED, + RELAYER_PROVED, + RELAYER_CLAIMED, + REFUNDED, +} + +export const useTxRefundStatus = ( + txId: string | undefined, + routerAddress: Address, + chain: Chain, + checkForRefund: boolean +) => { + const [isRefunded, setIsRefunded] = useState(false) + const currentTime = useIntervalTimer(600000) + + const getTxRefundStatus = async () => { + try { + const bridgeContract = await getRFQBridgeContract( + routerAddress, + chain?.id + ) + + const status = await checkRFQTxBridgeStatus( + txId, + bridgeContract as Address, + chain?.id + ) + + if (status === BridgeStatus.REFUNDED) { + setIsRefunded(true) + } + } catch (error) { + console.error('Failed to get transaction refund status:', error) + } + } + + useEffect(() => { + if (checkForRefund) { + getTxRefundStatus() + } + }, [checkForRefund, txId, chain, currentTime]) + + return isRefunded +} + +const getRFQBridgeContract = async ( + routerAddress: Address, + chainId: number +): Promise => { + try { + const fastBridgeAddress = await readContract(wagmiConfig, { + abi: fastBridgeRouterAbi, + address: routerAddress, + functionName: 'fastBridge', + chainId, + }) + + if (!isString(fastBridgeAddress)) { + throw new Error('Invalid address') + } + + return fastBridgeAddress + } catch (error) { + throw new Error(error) + } +} + +const checkRFQTxBridgeStatus = async ( + txId: string, + bridgeContract: Address, + chainId: number +): Promise => { + try { + const status = await readContract(wagmiConfig, { + abi: fastBridgeAbi, + address: bridgeContract, + functionName: 'bridgeStatuses', + args: [txId], + chainId, + }) + + if (!isNumber(status)) { + throw new Error('Invalid status code') + } + + return status + } catch (error) { + throw new Error(error) + } +} diff --git a/packages/synapse-interface/components/buttons/TransactionButton.tsx b/packages/synapse-interface/components/buttons/TransactionButton.tsx index e868868dc8..24461d6526 100644 --- a/packages/synapse-interface/components/buttons/TransactionButton.tsx +++ b/packages/synapse-interface/components/buttons/TransactionButton.tsx @@ -12,6 +12,7 @@ const baseClassNames = { disabled: 'disabled:opacity-50 disabled:cursor-not-allowed', background: 'bg-zinc-400 dark:bg-bgLight', gradient: 'enabled:bg-gradient-to-r', + transition: 'transition', } export const TransactionButton = ({ @@ -42,8 +43,8 @@ export const TransactionButton = ({ style={style} disabled={disabled} className={` - ${className} ${joinClassNames(baseClassNames)} + ${className} ${ isPending ? 'from-fuchsia-400 dark:from-fuchsia-900 to-purple-400 dark:to-purple-900' diff --git a/packages/synapse-interface/components/ui/AmountInput.tsx b/packages/synapse-interface/components/ui/AmountInput.tsx index ed352f5176..aebc3fd3a7 100644 --- a/packages/synapse-interface/components/ui/AmountInput.tsx +++ b/packages/synapse-interface/components/ui/AmountInput.tsx @@ -11,6 +11,7 @@ interface AmountInputTypes { showValue: string handleFromValueChange?: (event: React.ChangeEvent) => void setIsTyping?: (isTyping: boolean) => void + className?: string } export function AmountInput({ @@ -20,6 +21,7 @@ export function AmountInput({ showValue, handleFromValueChange, setIsTyping, + className, }: AmountInputTypes) { const debouncedSetIsTyping = useCallback( debounce((value: boolean) => setIsTyping?.(value), 600), @@ -38,6 +40,7 @@ export function AmountInput({ placeholder: 'placeholder:text-zinc-500 placeholder:dark:text-zinc-400', font: 'text-xl md:text-2xl font-medium', focus: 'focus:outline-none focus:ring-0 focus:border-none', + custom: className, } return ( diff --git a/packages/synapse-interface/components/ui/ChainSelector.tsx b/packages/synapse-interface/components/ui/ChainSelector.tsx index 3eee869fc6..87dab012d6 100644 --- a/packages/synapse-interface/components/ui/ChainSelector.tsx +++ b/packages/synapse-interface/components/ui/ChainSelector.tsx @@ -45,7 +45,7 @@ export function ChainSelector({ newToChainId: chainId, } - segmentAnalyticsEvent(eventTitle, eventData) + segmentAnalyticsEvent(eventTitle, eventData, true) dispatch(setFunction(chainId)) } } diff --git a/packages/synapse-interface/constants/abis/fastBridge.json b/packages/synapse-interface/constants/abis/fastBridge.json new file mode 100644 index 0000000000..2efeb97a4b --- /dev/null +++ b/packages/synapse-interface/constants/abis/fastBridge.json @@ -0,0 +1,851 @@ +[ + { + "type": "constructor", + "inputs": [ + { "name": "_owner", "type": "address", "internalType": "address" } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "DEFAULT_ADMIN_ROLE", + "inputs": [], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "DISPUTE_PERIOD", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "FEE_BPS", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "FEE_RATE_MAX", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "GOVERNOR_ROLE", + "inputs": [], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "GUARD_ROLE", + "inputs": [], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "MIN_DEADLINE_PERIOD", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "REFUNDER_ROLE", + "inputs": [], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "REFUND_DELAY", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "RELAYER_ROLE", + "inputs": [], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "bridge", + "inputs": [ + { + "name": "params", + "type": "tuple", + "internalType": "struct IFastBridge.BridgeParams", + "components": [ + { + "name": "dstChainId", + "type": "uint32", + "internalType": "uint32" + }, + { "name": "sender", "type": "address", "internalType": "address" }, + { "name": "to", "type": "address", "internalType": "address" }, + { + "name": "originToken", + "type": "address", + "internalType": "address" + }, + { + "name": "destToken", + "type": "address", + "internalType": "address" + }, + { + "name": "originAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "destAmount", + "type": "uint256", + "internalType": "uint256" + }, + { "name": "sendChainGas", "type": "bool", "internalType": "bool" }, + { "name": "deadline", "type": "uint256", "internalType": "uint256" } + ] + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "bridgeProofs", + "inputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "outputs": [ + { "name": "timestamp", "type": "uint96", "internalType": "uint96" }, + { "name": "relayer", "type": "address", "internalType": "address" } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "bridgeRelays", + "inputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "bridgeStatuses", + "inputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "outputs": [ + { + "name": "", + "type": "uint8", + "internalType": "enum FastBridge.BridgeStatus" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "canClaim", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "internalType": "bytes32" + }, + { "name": "relayer", "type": "address", "internalType": "address" } + ], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "chainGasAmount", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "claim", + "inputs": [ + { "name": "request", "type": "bytes", "internalType": "bytes" }, + { "name": "to", "type": "address", "internalType": "address" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "deployBlock", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "dispute", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "getBridgeTransaction", + "inputs": [ + { "name": "request", "type": "bytes", "internalType": "bytes" } + ], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct IFastBridge.BridgeTransaction", + "components": [ + { + "name": "originChainId", + "type": "uint32", + "internalType": "uint32" + }, + { + "name": "destChainId", + "type": "uint32", + "internalType": "uint32" + }, + { + "name": "originSender", + "type": "address", + "internalType": "address" + }, + { + "name": "destRecipient", + "type": "address", + "internalType": "address" + }, + { + "name": "originToken", + "type": "address", + "internalType": "address" + }, + { + "name": "destToken", + "type": "address", + "internalType": "address" + }, + { + "name": "originAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "destAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "originFeeAmount", + "type": "uint256", + "internalType": "uint256" + }, + { "name": "sendChainGas", "type": "bool", "internalType": "bool" }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { "name": "nonce", "type": "uint256", "internalType": "uint256" } + ] + } + ], + "stateMutability": "pure" + }, + { + "type": "function", + "name": "getRoleAdmin", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" } + ], + "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getRoleMember", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" }, + { "name": "index", "type": "uint256", "internalType": "uint256" } + ], + "outputs": [{ "name": "", "type": "address", "internalType": "address" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getRoleMemberCount", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" } + ], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "grantRole", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" }, + { "name": "account", "type": "address", "internalType": "address" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "hasRole", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" }, + { "name": "account", "type": "address", "internalType": "address" } + ], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "nonce", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "protocolFeeRate", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "protocolFees", + "inputs": [{ "name": "", "type": "address", "internalType": "address" }], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "prove", + "inputs": [ + { "name": "request", "type": "bytes", "internalType": "bytes" }, + { "name": "destTxHash", "type": "bytes32", "internalType": "bytes32" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "refund", + "inputs": [ + { "name": "request", "type": "bytes", "internalType": "bytes" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "relay", + "inputs": [ + { "name": "request", "type": "bytes", "internalType": "bytes" } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "renounceRole", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" }, + { + "name": "callerConfirmation", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "revokeRole", + "inputs": [ + { "name": "role", "type": "bytes32", "internalType": "bytes32" }, + { "name": "account", "type": "address", "internalType": "address" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setChainGasAmount", + "inputs": [ + { + "name": "newChainGasAmount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setProtocolFeeRate", + "inputs": [ + { "name": "newFeeRate", "type": "uint256", "internalType": "uint256" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "supportsInterface", + "inputs": [ + { "name": "interfaceId", "type": "bytes4", "internalType": "bytes4" } + ], + "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "sweepProtocolFees", + "inputs": [ + { "name": "token", "type": "address", "internalType": "address" }, + { "name": "recipient", "type": "address", "internalType": "address" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "BridgeDepositClaimed", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "relayer", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "token", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BridgeDepositRefunded", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "token", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BridgeProofDisputed", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "relayer", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BridgeProofProvided", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "relayer", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "transactionHash", + "type": "bytes32", + "indexed": false, + "internalType": "bytes32" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BridgeRelayed", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "relayer", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "originChainId", + "type": "uint32", + "indexed": false, + "internalType": "uint32" + }, + { + "name": "originToken", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "destToken", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "originAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "destAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "chainGasAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BridgeRequested", + "inputs": [ + { + "name": "transactionId", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "request", + "type": "bytes", + "indexed": false, + "internalType": "bytes" + }, + { + "name": "destChainId", + "type": "uint32", + "indexed": false, + "internalType": "uint32" + }, + { + "name": "originToken", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "destToken", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "originAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "destAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "sendChainGas", + "type": "bool", + "indexed": false, + "internalType": "bool" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ChainGasAmountUpdated", + "inputs": [ + { + "name": "oldChainGasAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "newChainGasAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "FeeRateUpdated", + "inputs": [ + { + "name": "oldFeeRate", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "newFeeRate", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "FeesSwept", + "inputs": [ + { + "name": "token", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "recipient", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RoleAdminChanged", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "previousAdminRole", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "newAdminRole", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RoleGranted", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "account", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RoleRevoked", + "inputs": [ + { + "name": "role", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "account", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { "type": "error", "name": "AccessControlBadConfirmation", "inputs": [] }, + { + "type": "error", + "name": "AccessControlUnauthorizedAccount", + "inputs": [ + { "name": "account", "type": "address", "internalType": "address" }, + { "name": "neededRole", "type": "bytes32", "internalType": "bytes32" } + ] + }, + { + "type": "error", + "name": "AddressEmptyCode", + "inputs": [ + { "name": "target", "type": "address", "internalType": "address" } + ] + }, + { + "type": "error", + "name": "AddressInsufficientBalance", + "inputs": [ + { "name": "account", "type": "address", "internalType": "address" } + ] + }, + { "type": "error", "name": "AmountIncorrect", "inputs": [] }, + { "type": "error", "name": "ChainIncorrect", "inputs": [] }, + { "type": "error", "name": "DeadlineExceeded", "inputs": [] }, + { "type": "error", "name": "DeadlineNotExceeded", "inputs": [] }, + { "type": "error", "name": "DeadlineTooShort", "inputs": [] }, + { "type": "error", "name": "DisputePeriodNotPassed", "inputs": [] }, + { "type": "error", "name": "DisputePeriodPassed", "inputs": [] }, + { "type": "error", "name": "FailedInnerCall", "inputs": [] }, + { "type": "error", "name": "MsgValueIncorrect", "inputs": [] }, + { + "type": "error", + "name": "SafeERC20FailedOperation", + "inputs": [ + { "name": "token", "type": "address", "internalType": "address" } + ] + }, + { "type": "error", "name": "SenderIncorrect", "inputs": [] }, + { "type": "error", "name": "StatusIncorrect", "inputs": [] }, + { "type": "error", "name": "TokenNotContract", "inputs": [] }, + { "type": "error", "name": "TransactionRelayed", "inputs": [] }, + { "type": "error", "name": "ZeroAddress", "inputs": [] } +] diff --git a/packages/synapse-interface/constants/abis/fastBridgeRouter.json b/packages/synapse-interface/constants/abis/fastBridgeRouter.json new file mode 100644 index 0000000000..c5617fe1cf --- /dev/null +++ b/packages/synapse-interface/constants/abis/fastBridgeRouter.json @@ -0,0 +1,387 @@ +[ + { + "type": "constructor", + "inputs": [ + { + "name": "owner_", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "receive", + "stateMutability": "payable" + }, + { + "type": "function", + "name": "GAS_REBATE_FLAG", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes1", + "internalType": "bytes1" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "adapterSwap", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "amountOut", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "bridge", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "chainId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "originQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + }, + { + "name": "destQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "fastBridge", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getOriginAmountOut", + "inputs": [ + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "rfqTokens", + "type": "address[]", + "internalType": "address[]" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "originQueries", + "type": "tuple[]", + "internalType": "struct SwapQuery[]", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setFastBridge", + "inputs": [ + { + "name": "fastBridge_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setSwapQuoter", + "inputs": [ + { + "name": "swapQuoter_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "swapQuoter", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "FastBridgeSet", + "inputs": [ + { + "name": "newFastBridge", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SwapQuoterSet", + "inputs": [ + { + "name": "newSwapQuoter", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "DeadlineExceeded", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientOutputAmount", + "inputs": [] + }, + { + "type": "error", + "name": "MsgValueIncorrect", + "inputs": [] + }, + { + "type": "error", + "name": "PoolNotFound", + "inputs": [] + }, + { + "type": "error", + "name": "TokenAddressMismatch", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotContract", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotETH", + "inputs": [] + }, + { + "type": "error", + "name": "TokensIdentical", + "inputs": [] + } +] diff --git a/packages/synapse-interface/contexts/UserProvider.tsx b/packages/synapse-interface/contexts/UserProvider.tsx index 6476d8e19e..82323c3402 100644 --- a/packages/synapse-interface/contexts/UserProvider.tsx +++ b/packages/synapse-interface/contexts/UserProvider.tsx @@ -21,12 +21,16 @@ export const UserProvider = ({ children }) => { useAccountEffect({ onConnect() { - segmentAnalyticsEvent(`[Wallet Analytics] connects`, { - walletId: connector?.id, - chainId: chain?.id, - query, - pathname, - }) + segmentAnalyticsEvent( + `[Wallet Analytics] connects`, + { + walletId: connector?.id, + chainId: chain?.id, + query, + pathname, + }, + true + ) }, onDisconnect() { segmentAnalyticsEvent('[Wallet Analytics] disconnect', {}) diff --git a/packages/synapse-interface/messages/ar.json b/packages/synapse-interface/messages/ar.json index b52b6f617d..a98de789cf 100644 --- a/packages/synapse-interface/messages/ar.json +++ b/packages/synapse-interface/messages/ar.json @@ -57,7 +57,8 @@ "ReceiveWithEllipsis": "استلام…", "All receivable tokens": "جميع الرموز القابلة للاستلام", "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "تم العثور على مسار لجسر {debouncedFromValue} {fromToken} على {fromChainId} إلى {toToken} على {toChainId}", - "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "لم يتم العثور على مسار لجسر {debouncedFromValue} {fromToken} على {fromChainId} إلى {toToken} على {toChainId}" + "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "لم يتم العثور على مسار لجسر {debouncedFromValue} {fromToken} على {fromChainId} إلى {toToken} على {toChainId}", + "Confirm new quote": "تأكيد العرض الجديد" }, "Completed": { "to": "إلى", @@ -305,7 +306,9 @@ "Began": "بدأ", "Complete": "مكتمل", "Reverted": "تم الرجوع", + "Refunded": "تم استرداده", "Transaction reverted, funds returned": "تم الرجوع عن المعاملة، تم إرجاع الأموال", + "Transaction refunded, funds returned": "تم استرداد المعاملة، وتمت إعادة الأموال", "What's taking so long?": "لماذا يستغرق الأمر وقتًا طويلًا؟", "FAQ": "الأسئلة الشائعة", "Support": "الدعم", diff --git a/packages/synapse-interface/messages/en-US.json b/packages/synapse-interface/messages/en-US.json index 4784ee82d4..8ffc76e4a1 100644 --- a/packages/synapse-interface/messages/en-US.json +++ b/packages/synapse-interface/messages/en-US.json @@ -57,7 +57,8 @@ "ReceiveWithEllipsis": "Receive…", "All receivable tokens": "All receivable tokens", "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}", - "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}" + "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}", + "Confirm new quote": "Confirm new quote" }, "Completed": { "to": "to", @@ -305,7 +306,9 @@ "Began": "Began", "Complete": "Complete", "Reverted": "Reverted", + "Refunded": "Refunded", "Transaction reverted, funds returned": "Transaction reverted, funds returned", + "Transaction refunded, funds returned": "Transaction refunded, funds returned", "What's taking so long?": "What's taking so long?", "FAQ": "F.A.Q", "Support": "Support", diff --git a/packages/synapse-interface/messages/es.json b/packages/synapse-interface/messages/es.json index dee3724b09..d19e0158c7 100644 --- a/packages/synapse-interface/messages/es.json +++ b/packages/synapse-interface/messages/es.json @@ -57,7 +57,8 @@ "ReceiveWithEllipsis": "Recibir…", "All receivable tokens": "Todos los tokens recibibles", "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "Ruta encontrada para el puente de {debouncedFromValue} {fromToken} en {fromChainId} a {toToken} en {toChainId}", - "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "No se encontró ruta para el puente de {debouncedFromValue} {fromToken} en {fromChainId} a {toToken} en {toChainId}" + "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "No se encontró ruta para el puente de {debouncedFromValue} {fromToken} en {fromChainId} a {toToken} en {toChainId}", + "Confirm new quote": "Confirmar nueva cotización" }, "Completed": { "to": "a", @@ -305,7 +306,9 @@ "Began": "Comenzó", "Complete": "Completo", "Reverted": "Revertido", + "Refunded": "Reembolsado", "Transaction reverted, funds returned": "Transacción revertida, fondos devueltos", + "Transaction refunded, funds returned": "Transacción reembolsada, fondos devueltos", "What's taking so long?": "¿Por qué está tardando tanto?", "FAQ": "Preguntas frecuentes", "Support": "Soporte", diff --git a/packages/synapse-interface/messages/fr.json b/packages/synapse-interface/messages/fr.json index 2541e4c68f..8164e65434 100644 --- a/packages/synapse-interface/messages/fr.json +++ b/packages/synapse-interface/messages/fr.json @@ -57,7 +57,8 @@ "ReceiveWithEllipsis": "Recevoir…", "All receivable tokens": "Tous les jetons recevables", "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "Route trouvée pour le transfert de {debouncedFromValue} {fromToken} sur {fromChainId} vers {toToken} sur {toChainId}", - "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "Aucune route trouvée pour le transfert de {debouncedFromValue} {fromToken} sur {fromChainId} vers {toToken} sur {toChainId}" + "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "Aucune route trouvée pour le transfert de {debouncedFromValue} {fromToken} sur {fromChainId} vers {toToken} sur {toChainId}", + "Confirm new quote": "Confirmer la nouvelle offre" }, "Completed": { "to": "vers", @@ -305,7 +306,9 @@ "Began": "Commencé", "Complete": "Terminé", "Reverted": "Annulé", + "Refunded": "Remboursé", "Transaction reverted, funds returned": "Transaction annulée, fonds retournés", + "Transaction refunded, funds returned": "Transaction remboursée, fonds retournés", "What's taking so long?": "Qu'est-ce qui prend tant de temps ?", "FAQ": "FAQ", "Support": "Support", diff --git a/packages/synapse-interface/messages/jp.json b/packages/synapse-interface/messages/jp.json index 1d23dd39a9..b5b97ef750 100644 --- a/packages/synapse-interface/messages/jp.json +++ b/packages/synapse-interface/messages/jp.json @@ -57,7 +57,8 @@ "ReceiveWithEllipsis": "受取…", "All receivable tokens": "すべての受取可能トークン", "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "{fromChainId}の{debouncedFromValue} {fromToken}から{toChainId}の{toToken}へのブリッジのルートが見つかりました", - "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "{fromChainId}の{debouncedFromValue} {fromToken}から{toChainId}の{toToken}へのブリッジのルートが見つかりませんでした" + "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "{fromChainId}の{debouncedFromValue} {fromToken}から{toChainId}の{toToken}へのブリッジのルートが見つかりませんでした", + "Confirm new quote": "新しい見積もりを確認" }, "Completed": { "to": "へ", @@ -305,7 +306,9 @@ "Began": "開始", "Complete": "完了", "Reverted": "取り消し", + "Refunded": "返金済み", "Transaction reverted, funds returned": "取引が取り消されました。資金が返還されました", + "Transaction refunded, funds returned": "取引が返金され、資金が戻されました", "What's taking so long?": "なぜ時間がかかっているのですか?", "FAQ": "よくある質問", "Support": "サポート", diff --git a/packages/synapse-interface/messages/tr.json b/packages/synapse-interface/messages/tr.json index 34ad00a1f9..2afcf1ec6b 100644 --- a/packages/synapse-interface/messages/tr.json +++ b/packages/synapse-interface/messages/tr.json @@ -57,7 +57,8 @@ "ReceiveWithEllipsis": "Al…", "All receivable tokens": "Tüm alınabilir tokenler", "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "{fromChainId}'deki {debouncedFromValue} {fromToken}'ı {toChainId}'deki {toToken}'a köprüleme için rota bulundu", - "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "{fromChainId}'deki {debouncedFromValue} {fromToken}'ı {toChainId}'deki {toToken}'a köprüleme için rota bulunamadı" + "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "{fromChainId}'deki {debouncedFromValue} {fromToken}'ı {toChainId}'deki {toToken}'a köprüleme için rota bulunamadı", + "Confirm new quote": "Yeni teklifi onayla" }, "Completed": { "to": "nereye", @@ -305,7 +306,9 @@ "Began": "Başladı", "Complete": "Tamamlandı", "Reverted": "Geri Alındı", + "Refunded": "İade edildi", "Transaction reverted, funds returned": "İşlem geri alındı, fonlar iade edildi", + "Transaction refunded, funds returned": "İşlem iade edildi, fonlar geri gönderildi", "What's taking so long?": "Neden bu kadar uzun sürüyor?", "FAQ": "SSS", "Support": "Destek", diff --git a/packages/synapse-interface/messages/zh-CN.json b/packages/synapse-interface/messages/zh-CN.json index 34a2b7500d..639c863131 100644 --- a/packages/synapse-interface/messages/zh-CN.json +++ b/packages/synapse-interface/messages/zh-CN.json @@ -57,7 +57,8 @@ "ReceiveWithEllipsis": "接收…", "All receivable tokens": "所有可接收代币", "Route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "找到桥接路线:从 {fromChainId} 的 {fromToken} 到 {toChainId} 的 {toToken}", - "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "未找到桥接路线:从 {fromChainId} 的 {fromToken} 到 {toChainId} 的 {toToken}" + "No route found for bridging {debouncedFromValue} {fromToken} on {fromChainId} to {toToken} on {toChainId}": "未找到桥接路线:从 {fromChainId} 的 {fromToken} 到 {toChainId} 的 {toToken}", + "Confirm new quote": "确认新报价" }, "Completed": { "to": "到", @@ -305,7 +306,9 @@ "Began": "已开始", "Complete": "已完成", "Reverted": "已回退", + "Refunded": "已退款", "Transaction reverted, funds returned": "交易已回退,资金已退还", + "Transaction refunded, funds returned": "交易已退款,资金已退还", "What's taking so long?": "为什么这么久?", "FAQ": "常见问题", "Support": "支持", diff --git a/packages/synapse-interface/package.json b/packages/synapse-interface/package.json index 73c7d036b0..a620c8472d 100644 --- a/packages/synapse-interface/package.json +++ b/packages/synapse-interface/package.json @@ -1,6 +1,6 @@ { "name": "@synapsecns/synapse-interface", - "version": "0.38.8", + "version": "0.40.3", "private": true, "engines": { "node": ">=18.18.0" @@ -37,7 +37,7 @@ "@reduxjs/toolkit": "^1.9.5", "@rtk-query/graphql-request-base-query": "^2.2.0", "@segment/analytics-next": "^1.53.0", - "@synapsecns/sdk-router": "^0.11.1", + "@synapsecns/sdk-router": "^0.11.2", "@tailwindcss/aspect-ratio": "^0.4.2", "@tailwindcss/forms": "^0.5.3", "@tailwindcss/typography": "^0.5.9", diff --git a/packages/synapse-interface/pages/state-managed-bridge/index.tsx b/packages/synapse-interface/pages/state-managed-bridge/index.tsx index 87f503de4f..2a63c4ba56 100644 --- a/packages/synapse-interface/pages/state-managed-bridge/index.tsx +++ b/packages/synapse-interface/pages/state-managed-bridge/index.tsx @@ -58,7 +58,6 @@ import { RootState } from '@/store/store' import { getUnixTimeMinutesFromNow } from '@/utils/time' import { isTransactionReceiptError } from '@/utils/isTransactionReceiptError' import { wagmiConfig } from '@/wagmiConfig' -import { useStaleQuoteUpdater } from '@/utils/hooks/useStaleQuoteUpdater' import { useMaintenance } from '@/components/Maintenance/Maintenance' import { screenAddress } from '@/utils/screenAddress' import { useWalletState } from '@/slices/wallet/hooks' @@ -66,10 +65,14 @@ import { useBridgeQuoteState } from '@/slices/bridgeQuote/hooks' import { resetBridgeQuote } from '@/slices/bridgeQuote/reducer' import { fetchBridgeQuote } from '@/slices/bridgeQuote/thunks' import { useIsBridgeApproved } from '@/utils/hooks/useIsBridgeApproved' +import { isTransactionUserRejectedError } from '@/utils/isTransactionUserRejectedError' +import { BridgeQuoteResetTimer } from '@/components/StateManagedBridge/BridgeQuoteResetTimer' +import { useBridgeValidations } from '@/components/StateManagedBridge/hooks/useBridgeValidations' +import { useStaleQuoteUpdater } from '@/components/StateManagedBridge/hooks/useStaleQuoteUpdater' const StateManagedBridge = () => { const dispatch = useAppDispatch() - const { address } = useAccount() + const { address, isConnected } = useAccount() const { synapseSDK } = useSynapseContext() const router = useRouter() const { query, pathname } = router @@ -96,6 +99,8 @@ const StateManagedBridge = () => { const isApproved = useIsBridgeApproved() + const { hasValidQuote, hasSufficientBalance } = useBridgeValidations() + const { isWalletPending } = useWalletState() const { showSettingsSlideOver } = useSelector( @@ -110,11 +115,15 @@ const StateManagedBridge = () => { } = useMaintenance() useEffect(() => { - segmentAnalyticsEvent(`[Bridge page] arrives`, { - fromChainId, - query, - pathname, - }) + segmentAnalyticsEvent( + `[Bridge page] arrives`, + { + fromChainId, + query, + pathname, + }, + true + ) }, [query]) useEffect(() => { @@ -137,8 +146,6 @@ const StateManagedBridge = () => { // will have to handle deadlineMinutes here at later time, gets passed as optional last arg in .bridgeQuote() - /* clear stored bridge quote before requesting new bridge quote */ - dispatch(resetBridgeQuote()) const currentTimestamp: number = getUnixTimeMinutesFromNow(0) try { @@ -217,11 +224,18 @@ const StateManagedBridge = () => { } } - useStaleQuoteUpdater( + const isUpdaterEnabled = + isConnected && + hasValidQuote && + hasSufficientBalance && + isApproved && + !isBridgePaused && + !isWalletPending + + const isQuoteStale = useStaleQuoteUpdater( bridgeQuote, getAndSetBridgeQuote, - isLoading, - isWalletPending, + isUpdaterEnabled, quoteTimeout ) @@ -288,6 +302,7 @@ const StateManagedBridge = () => { estimatedTime: bridgeQuote.estimatedTime, bridgeModuleName: bridgeQuote.bridgeModuleName, destinationAddress: destinationAddress, + routerAddress: bridgeQuote.routerAddress, }) ) try { @@ -424,6 +439,10 @@ const StateManagedBridge = () => { ) } + if (isTransactionUserRejectedError(error)) { + getAndSetBridgeQuote() + } + return txErrorHandler(error) } finally { dispatch(setIsWalletPending(false)) @@ -467,18 +486,29 @@ const StateManagedBridge = () => { }} disabled={isWalletPending} /> - + - +
+ +
+ +
+
)} diff --git a/packages/synapse-interface/pages/swap/index.tsx b/packages/synapse-interface/pages/swap/index.tsx index 4063348d2b..f3462cc76d 100644 --- a/packages/synapse-interface/pages/swap/index.tsx +++ b/packages/synapse-interface/pages/swap/index.tsx @@ -90,11 +90,15 @@ const StateManagedSwap = () => { const dispatch = useAppDispatch() useEffect(() => { - segmentAnalyticsEvent(`[Swap page] arrives`, { - swapChainId, - query, - pathname, - }) + segmentAnalyticsEvent( + `[Swap page] arrives`, + { + swapChainId, + query, + pathname, + }, + true + ) }, [query]) useEffect(() => { diff --git a/packages/synapse-interface/public/blacklist.json b/packages/synapse-interface/public/blacklist.json index 123e342197..22d5d66bd1 100644 --- a/packages/synapse-interface/public/blacklist.json +++ b/packages/synapse-interface/public/blacklist.json @@ -539,5 +539,17 @@ "0x610Df4B3D1f8528073d8710b6BdBbb86121ac6f3", "0x93c95714fbd1d9184f22aa82785478ff4e71a8e2", "0x800f06b394da4c98e88a0da09f0fb1eb280f9636", - "0xf7e8033366166f92eb477b7b38e0d47d47b43326" + "0xf7e8033366166f92eb477b7b38e0d47d47b43326", + "0xc01f4307DAa6A2d9e01303B194E77fBEF29Fe904", + "0xFAF26EdB4E4b43B7A513808d59913FC43A705D74", + "0x278dF4492d16321b247660799FAD1A12dE152Dd1", + "0x79c7c3D7CBe8A8354CD1401A8718a7dBF65301d2", + "0x3b00Daad3DE070f3060812e0F8D6b470B9F64Fa2", + "0xeB428AdEF88EcFFbd819bD7Afa85d41b7fAa0510", + "0xf97823da25d1e4051ca2bf4ffbe6b788df0628cb", + "0x105c3fB416146c18AD96a470140BEAE8fb9cf950", + "0xd4d582840cc2bf3d1a6cc94b10355005ceeee2df", + "0x408d8e12c7ed8e5a7291fbD5E6164f41ecdA6B46", + "0x278dF4492d16321b247660799FAD1A12dE152Dd1", + "0x551BE68Cdf9Ce453ead61097649C34196d0bDb27" ] diff --git a/packages/synapse-interface/slices/_transactions/reducer.ts b/packages/synapse-interface/slices/_transactions/reducer.ts index 2225c8ead0..cac413c616 100644 --- a/packages/synapse-interface/slices/_transactions/reducer.ts +++ b/packages/synapse-interface/slices/_transactions/reducer.ts @@ -14,10 +14,11 @@ export interface _TransactionDetails { originValue: string originTxHash: string bridgeModuleName: string + routerAddress: string estimatedTime: number timestamp: number kappa?: string - status: 'pending' | 'completed' | 'reverted' + status: 'pending' | 'completed' | 'reverted' | 'refunded' } export interface _TransactionsState { @@ -87,6 +88,19 @@ export const transactionsSlice = createSlice({ state.transactions[txIndex].status = 'reverted' } }, + refundTransaction: ( + state, + action: PayloadAction<{ originTxHash: string }> + ) => { + const { originTxHash } = action.payload + + const txIndex = state.transactions.findIndex( + (tx) => tx.originTxHash === originTxHash + ) + if (txIndex !== -1) { + state.transactions[txIndex].status = 'refunded' + } + }, clearTransactions: (state) => { state.transactions = [] }, @@ -99,6 +113,7 @@ export const { updateTransactionKappa, completeTransaction, revertTransaction, + refundTransaction, clearTransactions, } = transactionsSlice.actions diff --git a/packages/synapse-interface/slices/bridgeQuote/reducer.ts b/packages/synapse-interface/slices/bridgeQuote/reducer.ts index 8407876035..898769f622 100644 --- a/packages/synapse-interface/slices/bridgeQuote/reducer.ts +++ b/packages/synapse-interface/slices/bridgeQuote/reducer.ts @@ -6,11 +6,13 @@ import { fetchBridgeQuote } from './thunks' export interface BridgeQuoteState { bridgeQuote: BridgeQuote + previousBridgeQuote: BridgeQuote | null isLoading: boolean } export const initialState: BridgeQuoteState = { bridgeQuote: EMPTY_BRIDGE_QUOTE, + previousBridgeQuote: null, isLoading: false, } @@ -24,6 +26,9 @@ export const bridgeQuoteSlice = createSlice({ resetBridgeQuote: (state) => { state.bridgeQuote = initialState.bridgeQuote }, + setPreviousBridgeQuote: (state, action: PayloadAction) => { + state.previousBridgeQuote = action.payload + }, }, extraReducers: (builder) => { builder @@ -44,6 +49,7 @@ export const bridgeQuoteSlice = createSlice({ }, }) -export const { resetBridgeQuote, setIsLoading } = bridgeQuoteSlice.actions +export const { resetBridgeQuote, setIsLoading, setPreviousBridgeQuote } = + bridgeQuoteSlice.actions export default bridgeQuoteSlice.reducer diff --git a/packages/synapse-interface/slices/transactions/actions.ts b/packages/synapse-interface/slices/transactions/actions.ts index cb4ef22cc7..68400faaa9 100644 --- a/packages/synapse-interface/slices/transactions/actions.ts +++ b/packages/synapse-interface/slices/transactions/actions.ts @@ -16,6 +16,7 @@ export interface PendingBridgeTransaction { isSubmitted: boolean estimatedTime: number bridgeModuleName: string + routerAddress: string destinationAddress: Address | null } diff --git a/packages/synapse-interface/store/middleware/bridgeQuoteHistoryMiddleware.ts b/packages/synapse-interface/store/middleware/bridgeQuoteHistoryMiddleware.ts new file mode 100644 index 0000000000..f58c09ae03 --- /dev/null +++ b/packages/synapse-interface/store/middleware/bridgeQuoteHistoryMiddleware.ts @@ -0,0 +1,25 @@ +import { + Middleware, + MiddlewareAPI, + Dispatch, + AnyAction, +} from '@reduxjs/toolkit' + +export const bridgeQuoteHistoryMiddleware: Middleware = + (store: MiddlewareAPI) => (next: Dispatch) => (action: AnyAction) => { + const previousState = store.getState() + const result = next(action) + const currentState = store.getState() + + if ( + previousState.bridgeQuote.bridgeQuote !== + currentState.bridgeQuote.bridgeQuote + ) { + store.dispatch({ + type: 'bridgeQuote/setPreviousBridgeQuote', + payload: previousState.bridgeQuote.bridgeQuote, + }) + } + + return result + } diff --git a/packages/synapse-interface/store/destinationAddressMiddleware.ts b/packages/synapse-interface/store/middleware/destinationAddressMiddleware.ts similarity index 100% rename from packages/synapse-interface/store/destinationAddressMiddleware.ts rename to packages/synapse-interface/store/middleware/destinationAddressMiddleware.ts diff --git a/packages/synapse-interface/store/store.ts b/packages/synapse-interface/store/store.ts index 70f5515a6f..d8cdf3e70a 100644 --- a/packages/synapse-interface/store/store.ts +++ b/packages/synapse-interface/store/store.ts @@ -6,7 +6,8 @@ import { api } from '@/slices/api/slice' import { segmentAnalyticsEvent } from '@/contexts/SegmentAnalyticsProvider' import { storageKey, persistConfig, persistedReducer } from './reducer' import { resetReduxCache } from '@/slices/application/actions' -import { destinationAddressMiddleware } from '@/store/destinationAddressMiddleware' +import { destinationAddressMiddleware } from '@/store/middleware/destinationAddressMiddleware' +import { bridgeQuoteHistoryMiddleware } from './middleware/bridgeQuoteHistoryMiddleware' const checkVersionAndResetCache = (): boolean => { if (typeof window !== 'undefined') { @@ -28,7 +29,11 @@ export const store = configureStore({ middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false, - }).concat(api.middleware, destinationAddressMiddleware), + }).concat( + api.middleware, + destinationAddressMiddleware, + bridgeQuoteHistoryMiddleware + ), }) if (checkVersionAndResetCache()) { diff --git a/packages/synapse-interface/utils/getSignificantDecimals.ts b/packages/synapse-interface/utils/getSignificantDecimals.ts new file mode 100644 index 0000000000..90e29990e7 --- /dev/null +++ b/packages/synapse-interface/utils/getSignificantDecimals.ts @@ -0,0 +1,23 @@ +export const getSignificantDecimals = ( + numberString: string, + defaultDecimals: number = 2 +): number => { + const parts = numberString.split('.') + const decimalPart = parts[1] + + if (!decimalPart) return 0 + + if (/^0*$/.test(decimalPart)) { + return defaultDecimals + } + + let significantDecimals = 0 + + for (let i = 0; i < decimalPart.length; i++) { + if (decimalPart[i] !== '0') { + significantDecimals = i + 1 + } + } + + return significantDecimals +} diff --git a/packages/synapse-interface/utils/hooks/useCoingeckoPrice.ts b/packages/synapse-interface/utils/hooks/useCoingeckoPrice.ts index 604334008a..7bdfdda905 100644 --- a/packages/synapse-interface/utils/hooks/useCoingeckoPrice.ts +++ b/packages/synapse-interface/utils/hooks/useCoingeckoPrice.ts @@ -8,7 +8,7 @@ const ID_MAP = { GLMR: 'moonbeam', CANTO: 'canto', FTM: 'fantom', - METIS: 'metis-token', + Metis: 'metis-token', BNB: 'binancecoin', MATIC: 'matic-network', KLAY: 'klay-token', diff --git a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts b/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts deleted file mode 100644 index 05ccd37dc3..0000000000 --- a/packages/synapse-interface/utils/hooks/useStaleQuoteUpdater.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { isNull, isNumber } from 'lodash' -import { useEffect, useRef } from 'react' - -import { BridgeQuote } from '@/utils/types' -import { calculateTimeBetween } from '@/utils/time' -import { useIntervalTimer } from '@/utils/hooks/useIntervalTimer' -import { convertUuidToUnix } from '@/utils/convertUuidToUnix' - -/** - * Refreshes quotes based on selected stale timeout duration. - * Will refresh quote when browser is active and wallet prompt is not pending. - */ -export const useStaleQuoteUpdater = ( - quote: BridgeQuote, - refreshQuoteCallback: () => Promise, - isQuoteLoading: boolean, - isWalletPending: boolean, - staleTimeout: number = 15000 // Default 15_000ms or 15s -) => { - const eventListenerRef = useRef void)>(null) - - const quoteTime = quote?.id ? convertUuidToUnix(quote?.id) : null - const isValidQuote = isNumber(quoteTime) && !isNull(quoteTime) - - const currentTime = useIntervalTimer(staleTimeout, !isValidQuote) - - useEffect(() => { - if (isValidQuote && !isQuoteLoading && !isWalletPending) { - const timeDifference = calculateTimeBetween(currentTime, quoteTime) - const isStaleQuote = timeDifference >= staleTimeout - - if (isStaleQuote) { - if (eventListenerRef.current) { - document.removeEventListener('mousemove', eventListenerRef.current) - } - - const newEventListener = () => { - refreshQuoteCallback() - eventListenerRef.current = null - } - - document.addEventListener('mousemove', newEventListener, { - once: true, - }) - - eventListenerRef.current = newEventListener - } - } - }, [currentTime, staleTimeout]) -} diff --git a/packages/synapse-interface/utils/hooks/use_TransactionsListener.ts b/packages/synapse-interface/utils/hooks/use_TransactionsListener.ts index b82d6ca8fe..e2f7b85697 100644 --- a/packages/synapse-interface/utils/hooks/use_TransactionsListener.ts +++ b/packages/synapse-interface/utils/hooks/use_TransactionsListener.ts @@ -50,6 +50,7 @@ export const use_TransactionsListener = () => { destinationChain: tx.destinationChain, destinationToken: tx.destinationToken, bridgeModuleName: tx.bridgeModuleName, + routerAddress: tx.routerAddress, estimatedTime: tx.estimatedTime, timestamp: tx.id, status: 'pending', diff --git a/packages/synapse-interface/utils/screenAddress.ts b/packages/synapse-interface/utils/screenAddress.ts index 5f383a94e5..45e7aaa9c9 100644 --- a/packages/synapse-interface/utils/screenAddress.ts +++ b/packages/synapse-interface/utils/screenAddress.ts @@ -12,15 +12,18 @@ const createRiskDetectedEvent = (address: Address | string) => { }) } +const dispatchRiskDetectedEvent = (address: Address | string) => { + const event = createRiskDetectedEvent(address) + GlobalEventEmitter.dispatchEvent(event) +} + export const screenAddress = async ( address: Address | string ): Promise => { const url = `https://screener.omnirpc.io/fe/address/${address}` if (isBlacklisted(address)) { - const event = createRiskDetectedEvent(address) - - GlobalEventEmitter.dispatchEvent(event) + dispatchRiskDetectedEvent(address) return true } @@ -29,14 +32,13 @@ export const screenAddress = async ( const { risk } = await response.json() if (risk) { - const event = createRiskDetectedEvent(address) - - GlobalEventEmitter.dispatchEvent(event) + dispatchRiskDetectedEvent(address) return true + } else { + return false } - return false } catch (error) { - console.error('Error:', error) - return false + dispatchRiskDetectedEvent(address) + return true } } diff --git a/packages/synapse-interface/utils/time.ts b/packages/synapse-interface/utils/time.ts index 8e30b5075a..20a8c042f0 100644 --- a/packages/synapse-interface/utils/time.ts +++ b/packages/synapse-interface/utils/time.ts @@ -51,3 +51,7 @@ export const isTimestampToday = (unixTimestamp: number): boolean => { dateFromTimestamp.getFullYear() === currentDate.getFullYear() ) } + +export const convertMsToSeconds = (ms: number) => { + return Math.ceil(ms / 1000) +} diff --git a/packages/widget/CHANGELOG.md b/packages/widget/CHANGELOG.md index f4b030108b..5bfd1dc8af 100644 --- a/packages/widget/CHANGELOG.md +++ b/packages/widget/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.7.2](https://github.com/synapsecns/sanguine/compare/@synapsecns/widget@0.7.1...@synapsecns/widget@0.7.2) (2024-09-26) + +**Note:** Version bump only for package @synapsecns/widget + + + + + ## [0.7.1](https://github.com/synapsecns/sanguine/compare/@synapsecns/widget@0.7.0...@synapsecns/widget@0.7.1) (2024-09-04) **Note:** Version bump only for package @synapsecns/widget diff --git a/packages/widget/package.json b/packages/widget/package.json index eafb1d4c20..bff3072665 100644 --- a/packages/widget/package.json +++ b/packages/widget/package.json @@ -1,7 +1,7 @@ { "name": "@synapsecns/widget", "description": "Widget library for interacting with the Synapse Protocol", - "version": "0.7.1", + "version": "0.7.2", "license": "MIT", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", @@ -68,7 +68,7 @@ "@ethersproject/providers": "^5.7.2", "@ethersproject/units": "^5.7.0", "@reduxjs/toolkit": "^2.0.1", - "@synapsecns/sdk-router": "^0.11.1", + "@synapsecns/sdk-router": "^0.11.2", "ethers": "^6.9.1", "lodash": "^4.17.21", "react-redux": "^9.0.2" diff --git a/services/rfq/api/client/client.go b/services/rfq/api/client/client.go index ce6880b85e..1bcba494b5 100644 --- a/services/rfq/api/client/client.go +++ b/services/rfq/api/client/client.go @@ -4,11 +4,15 @@ package client import ( "context" + "encoding/json" "fmt" "net/http" "strconv" + "strings" "time" + "github.com/ipfs/go-log" + "github.com/google/uuid" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" @@ -17,17 +21,23 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/go-resty/resty/v2" + "github.com/gorilla/websocket" "github.com/synapsecns/sanguine/ethergo/signer/signer" "github.com/synapsecns/sanguine/services/rfq/api/model" "github.com/synapsecns/sanguine/services/rfq/api/rest" ) +var logger = log.Logger("rfq-client") + +const pingPeriod = 20 * time.Second + // AuthenticatedClient is an interface for the RFQ API. // It provides methods for creating, retrieving and updating quotes. type AuthenticatedClient interface { - PutQuote(ctx context.Context, q *model.PutQuoteRequest) error + PutQuote(ctx context.Context, q *model.PutRelayerQuoteRequest) error PutBulkQuotes(ctx context.Context, q *model.PutBulkQuotesRequest) error PutRelayAck(ctx context.Context, req *model.PutAckRequest) (*model.PutRelayAckResponse, error) + SubscribeActiveQuotes(ctx context.Context, req *model.SubscribeActiveRFQRequest, reqChan chan *model.ActiveRFQMessage) (respChan chan *model.ActiveRFQMessage, err error) UnauthenticatedClient } @@ -37,6 +47,7 @@ type UnauthenticatedClient interface { GetSpecificQuote(ctx context.Context, q *model.GetQuoteSpecificRequest) ([]*model.GetQuoteResponse, error) GetQuoteByRelayerAddress(ctx context.Context, relayerAddr string) ([]*model.GetQuoteResponse, error) GetRFQContracts(ctx context.Context) (*model.GetContractsResponse, error) + PutRFQRequest(ctx context.Context, q *model.PutRFQRequest) (*model.PutRFQResponse, error) resty() *resty.Client } @@ -50,7 +61,8 @@ func (c unauthenticatedClient) resty() *resty.Client { type clientImpl struct { UnauthenticatedClient - rClient *resty.Client + rClient *resty.Client + reqSigner signer.Signer } // NewAuthenticatedClient creates a new client for the RFQ quoting API. @@ -65,33 +77,40 @@ func NewAuthenticatedClient(metrics metrics.Handler, rfqURL string, reqSigner si // to a new variable for clarity. authedClient := unauthedClient.resty(). OnBeforeRequest(func(client *resty.Client, request *resty.Request) error { - // if request.Method == "PUT" && request.URL == rfqURL+rest.QUOTE_ROUTE { - // i.e. signature (hex encoded) = keccak(bytes.concat("\x19Ethereum Signed Message:\n", len(strconv.Itoa(time.Now().Unix()), strconv.Itoa(time.Now().Unix()))) - // so that full auth header string: auth = strconv.Itoa(time.Now().Unix()) + ":" + signature - // Get the current Unix timestamp as a string. - now := strconv.Itoa(int(time.Now().Unix())) - - // Prepare the data to be signed. - data := "\x19Ethereum Signed Message:\n" + strconv.Itoa(len(now)) + now - - sig, err := reqSigner.SignMessage(request.Context(), []byte(data), true) - + authHeader, err := getAuthHeader(request.Context(), reqSigner) if err != nil { - return fmt.Errorf("failed to sign request: %w", err) + return fmt.Errorf("failed to get auth header: %w", err) } - - res := fmt.Sprintf("%s:%s", now, hexutil.Encode(signer.Encode(sig))) - request.SetHeader("Authorization", res) - + request.SetHeader(rest.AuthorizationHeader, authHeader) return nil }) return &clientImpl{ UnauthenticatedClient: unauthedClient, rClient: authedClient, + reqSigner: reqSigner, }, nil } +func getAuthHeader(ctx context.Context, reqSigner signer.Signer) (string, error) { + // if request.Method == "PUT" && request.URL == rfqURL+rest.QUOTE_ROUTE { + // i.e. signature (hex encoded) = keccak(bytes.concat("\x19Ethereum Signed Message:\n", len(strconv.Itoa(time.Now().Unix()), strconv.Itoa(time.Now().Unix()))) + // so that full auth header string: auth = strconv.Itoa(time.Now().Unix()) + ":" + signature + // Get the current Unix timestamp as a string. + now := strconv.Itoa(int(time.Now().Unix())) + + // Prepare the data to be signed. + data := "\x19Ethereum Signed Message:\n" + strconv.Itoa(len(now)) + now + + sig, err := reqSigner.SignMessage(ctx, []byte(data), true) + + if err != nil { + return "", fmt.Errorf("failed to sign request: %w", err) + } + + return fmt.Sprintf("%s:%s", now, hexutil.Encode(signer.Encode(sig))), nil +} + // NewUnauthenticatedClient creates a new client for the RFQ quoting API. func NewUnauthenticatedClient(metricHandler metrics.Handler, rfqURL string) (UnauthenticatedClient, error) { client := resty.New(). @@ -115,7 +134,7 @@ func NewUnauthenticatedClient(metricHandler metrics.Handler, rfqURL string) (Una } // PutQuote puts a new quote in the RFQ quoting API. -func (c *clientImpl) PutQuote(ctx context.Context, q *model.PutQuoteRequest) error { +func (c *clientImpl) PutQuote(ctx context.Context, q *model.PutRelayerQuoteRequest) error { res, err := c.rClient.R(). SetContext(ctx). SetBody(q). @@ -159,6 +178,171 @@ func (c *clientImpl) PutRelayAck(ctx context.Context, req *model.PutAckRequest) return ack, nil } +func (c *clientImpl) SubscribeActiveQuotes(ctx context.Context, req *model.SubscribeActiveRFQRequest, reqChan chan *model.ActiveRFQMessage) (respChan chan *model.ActiveRFQMessage, err error) { + conn, err := c.connectWebsocket(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to connect to websocket: %w", err) + } + // first, subscrbe to the given chains + sub := model.SubscriptionParams{ + Chains: req.ChainIDs, + } + subJSON, err := json.Marshal(sub) + if err != nil { + return respChan, fmt.Errorf("error marshaling subscription params: %w", err) + } + err = conn.WriteJSON(model.ActiveRFQMessage{ + Op: rest.SubscribeOp, + Content: json.RawMessage(subJSON), + }) + if err != nil { + return nil, fmt.Errorf("error sending subscribe message: %w", err) + } + // make sure subscription is successful + var resp model.ActiveRFQMessage + err = conn.ReadJSON(&resp) + if err != nil { + return nil, fmt.Errorf("error reading subscribe response: %w", err) + } + if !resp.Success || resp.Op != rest.SubscribeOp { + return nil, fmt.Errorf("subscription failed") + } + + respChan = make(chan *model.ActiveRFQMessage) + go func() { + wsErr := c.processWebsocket(ctx, conn, reqChan, respChan) + if wsErr != nil { + logger.Error("Error running websocket listener: %s", wsErr) + } + }() + + return respChan, nil +} + +func (c *clientImpl) connectWebsocket(ctx context.Context, req *model.SubscribeActiveRFQRequest) (conn *websocket.Conn, err error) { + if len(req.ChainIDs) == 0 { + return nil, fmt.Errorf("chain IDs are required") + } + + header, err := c.getWsHeaders(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to get auth header: %w", err) + } + + reqURL := strings.Replace(c.rClient.BaseURL, "http", "ws", 1) + rest.RFQStreamRoute + conn, httpResp, err := websocket.DefaultDialer.Dial(reqURL, header) + if err != nil { + return nil, fmt.Errorf("failed to connect to websocket: %w", err) + } + err = httpResp.Body.Close() + if err != nil { + logger.Warnf("error closing websocket connection: %v", err) + } + + return conn, nil +} + +func (c *clientImpl) getWsHeaders(ctx context.Context, req *model.SubscribeActiveRFQRequest) (header http.Header, err error) { + header = http.Header{} + chainIDsJSON, err := json.Marshal(req.ChainIDs) + if err != nil { + return header, fmt.Errorf("failed to marshal chain IDs: %w", err) + } + header.Set(rest.ChainsHeader, string(chainIDsJSON)) + authHeader, err := getAuthHeader(ctx, c.reqSigner) + if err != nil { + return header, fmt.Errorf("failed to get auth header: %w", err) + } + header.Set(rest.AuthorizationHeader, authHeader) + return header, nil +} + +func (c *clientImpl) processWebsocket(ctx context.Context, conn *websocket.Conn, reqChan, respChan chan *model.ActiveRFQMessage) (err error) { + defer func() { + close(respChan) + err := conn.Close() + if err != nil { + logger.Warnf("error closing websocket connection: %v", err) + } + }() + + readChan := make(chan []byte) + go c.listenWsMessages(ctx, conn, readChan) + go c.sendPings(ctx, reqChan) + + for { + select { + case <-ctx.Done(): + return nil + case msg, ok := <-reqChan: + if !ok { + return fmt.Errorf("error reading from reqChan: %w", ctx.Err()) + } + err := conn.WriteJSON(msg) + if err != nil { + return fmt.Errorf("error sending message to websocket: %w", err) + } + case msg, ok := <-readChan: + if !ok { + return nil + } + err = c.handleWsMessage(ctx, msg, respChan) + if err != nil { + return fmt.Errorf("error handling websocket message: %w", err) + } + } + } +} + +func (c *clientImpl) sendPings(ctx context.Context, reqChan chan *model.ActiveRFQMessage) { + pingTicker := time.NewTicker(pingPeriod) + defer pingTicker.Stop() + + for { + select { + case <-pingTicker.C: + pingMsg := model.ActiveRFQMessage{ + Op: rest.PingOp, + } + reqChan <- &pingMsg + case <-ctx.Done(): + return + } + } +} +func (c *clientImpl) listenWsMessages(ctx context.Context, conn *websocket.Conn, readChan chan []byte) { + defer close(readChan) + for { + _, message, err := conn.ReadMessage() + if err != nil { + if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) { + logger.Warnf("websocket connection closed unexpectedly: %v", err) + } + return + } + select { + case readChan <- message: + case <-ctx.Done(): + return + } + } +} + +func (c *clientImpl) handleWsMessage(ctx context.Context, msg []byte, respChan chan *model.ActiveRFQMessage) (err error) { + var rfqMsg model.ActiveRFQMessage + err = json.Unmarshal(msg, &rfqMsg) + if err != nil { + return fmt.Errorf("error unmarshaling message: %w", err) + } + + select { + case respChan <- &rfqMsg: + case <-ctx.Done(): + return nil + } + return nil +} + // GetAllQuotes retrieves all quotes from the RFQ quoting API. func (c *unauthenticatedClient) GetAllQuotes(ctx context.Context) ([]*model.GetQuoteResponse, error) { var quotes []*model.GetQuoteResponse @@ -242,6 +426,25 @@ func (c unauthenticatedClient) GetRFQContracts(ctx context.Context) (*model.GetC return contracts, nil } +func (c unauthenticatedClient) PutRFQRequest(ctx context.Context, q *model.PutRFQRequest) (*model.PutRFQResponse, error) { + var response model.PutRFQResponse + resp, err := c.rClient.R(). + SetContext(ctx). + SetBody(q). + SetResult(&response). + Put(rest.RFQRoute) + + if err != nil { + return nil, fmt.Errorf("error from server: %s: %w", getStatus(resp), err) + } + + if resp.IsError() { + return nil, fmt.Errorf("error from server: %s", getStatus(resp)) + } + + return &response, nil +} + func getStatus(resp *resty.Response) string { if resp == nil { return "http status unavailable" diff --git a/services/rfq/api/client/client_test.go b/services/rfq/api/client/client_test.go index bfc8dc3483..0314f9018f 100644 --- a/services/rfq/api/client/client_test.go +++ b/services/rfq/api/client/client_test.go @@ -7,7 +7,7 @@ import ( // TODO: @aurelius tese tests make a lot less sesnes w/ a composite index func (c *ClientSuite) TestPutAndGetQuote() { - req := model.PutQuoteRequest{ + req := model.PutRelayerQuoteRequest{ OriginChainID: 1, OriginTokenAddr: "0xOriginTokenAddr", DestChainID: 42161, @@ -40,7 +40,7 @@ func (c *ClientSuite) TestPutAndGetQuote() { func (c *ClientSuite) TestPutAndGetBulkQuotes() { req := model.PutBulkQuotesRequest{ - Quotes: []model.PutQuoteRequest{ + Quotes: []model.PutRelayerQuoteRequest{ { OriginChainID: 1, OriginTokenAddr: "0xOriginTokenAddr", @@ -98,7 +98,7 @@ func (c *ClientSuite) TestPutAndGetBulkQuotes() { } func (c *ClientSuite) TestGetSpecificQuote() { - req := model.PutQuoteRequest{ + req := model.PutRelayerQuoteRequest{ OriginChainID: 1, OriginTokenAddr: "0xOriginTokenAddr", DestChainID: 42161, @@ -135,7 +135,7 @@ func (c *ClientSuite) TestGetSpecificQuote() { } func (c *ClientSuite) TestGetQuoteByRelayerAddress() { - req := model.PutQuoteRequest{ + req := model.PutRelayerQuoteRequest{ OriginChainID: 1, OriginTokenAddr: "0xOriginTokenAddr", DestChainID: 42161, diff --git a/services/rfq/api/db/activequoterequeststatus_string.go b/services/rfq/api/db/activequoterequeststatus_string.go new file mode 100644 index 0000000000..cb9e64a4d6 --- /dev/null +++ b/services/rfq/api/db/activequoterequeststatus_string.go @@ -0,0 +1,27 @@ +// Code generated by "stringer -type=ActiveQuoteRequestStatus"; DO NOT EDIT. + +package db + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Received-1] + _ = x[Pending-2] + _ = x[Expired-3] + _ = x[Closed-4] +} + +const _ActiveQuoteRequestStatus_name = "ReceivedPendingExpiredClosed" + +var _ActiveQuoteRequestStatus_index = [...]uint8{0, 8, 15, 22, 31} + +func (i ActiveQuoteRequestStatus) String() string { + i -= 1 + if i >= ActiveQuoteRequestStatus(len(_ActiveQuoteRequestStatus_index)-1) { + return "ActiveQuoteRequestStatus(" + strconv.FormatInt(int64(i+1), 10) + ")" + } + return _ActiveQuoteRequestStatus_name[_ActiveQuoteRequestStatus_index[i]:_ActiveQuoteRequestStatus_index[i+1]] +} diff --git a/services/rfq/api/db/activequoteresponsestatus_string.go b/services/rfq/api/db/activequoteresponsestatus_string.go new file mode 100644 index 0000000000..564f93f4c1 --- /dev/null +++ b/services/rfq/api/db/activequoteresponsestatus_string.go @@ -0,0 +1,28 @@ +// Code generated by "stringer -type=ActiveQuoteResponseStatus"; DO NOT EDIT. + +package db + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Considered-1] + _ = x[Returned-2] + _ = x[PastExpiration-3] + _ = x[Malformed-4] + _ = x[Duplicate-5] +} + +const _ActiveQuoteResponseStatus_name = "ConsideredReturnedPastExpirationMalformedDuplicate" + +var _ActiveQuoteResponseStatus_index = [...]uint8{0, 10, 18, 32, 41, 50} + +func (i ActiveQuoteResponseStatus) String() string { + i -= 1 + if i >= ActiveQuoteResponseStatus(len(_ActiveQuoteResponseStatus_index)-1) { + return "ActiveQuoteResponseStatus(" + strconv.FormatInt(int64(i+1), 10) + ")" + } + return _ActiveQuoteResponseStatus_name[_ActiveQuoteResponseStatus_index[i]:_ActiveQuoteResponseStatus_index[i+1]] +} diff --git a/services/rfq/api/db/api_db.go b/services/rfq/api/db/api_db.go index 48c7344484..44d9fb0a25 100644 --- a/services/rfq/api/db/api_db.go +++ b/services/rfq/api/db/api_db.go @@ -3,9 +3,13 @@ package db import ( "context" + "database/sql/driver" + "fmt" "time" "github.com/shopspring/decimal" + "github.com/synapsecns/sanguine/core/dbcommon" + "github.com/synapsecns/sanguine/services/rfq/api/model" ) // Quote is the database model for a quote. @@ -34,6 +38,172 @@ type Quote struct { UpdatedAt time.Time } +// ActiveQuoteRequestStatus is the status of a quote request in the db. +// This is the primary mechanism for moving data through the app. +// +// TODO: consider making this an interface and exporting that. +// +// EXTREMELY IMPORTANT: DO NOT ADD NEW VALUES TO THIS ENUM UNLESS THEY ARE AT THE END. +// +//go:generate go run golang.org/x/tools/cmd/stringer -type=ActiveQuoteRequestStatus +type ActiveQuoteRequestStatus uint8 + +const ( + // Received means the quote request has been received by the server. + Received ActiveQuoteRequestStatus = iota + 1 + // Pending means the quote request is pending awaiting relayer responses. + Pending + // Expired means the quote request has expired without any valid responses. + Expired + // Closed means the quote request has been fulfilled. + Closed +) + +// Int returns the int value of the quote request status. +func (q ActiveQuoteRequestStatus) Int() uint8 { + return uint8(q) +} + +// GormDataType implements the gorm common interface for enums. +func (q ActiveQuoteRequestStatus) GormDataType() string { + return dbcommon.EnumDataType +} + +// Scan implements the gorm common interface for enums. +func (q *ActiveQuoteRequestStatus) Scan(src any) error { + res, err := dbcommon.EnumScan(src) + if err != nil { + return fmt.Errorf("could not scan %w", err) + } + newStatus := ActiveQuoteRequestStatus(res) + *q = newStatus + return nil +} + +// Value implements the gorm common interface for enums. +func (q ActiveQuoteRequestStatus) Value() (driver.Value, error) { + // nolint: wrapcheck + return dbcommon.EnumValue(q) +} + +var _ dbcommon.Enum = (*ActiveQuoteRequestStatus)(nil) + +// ActiveQuoteResponseStatus is the status of a quote request in the db. +// This is the primary mechanism for moving data through the app. +// +// TODO: consider making this an interface and exporting that. +// +// EXTREMELY IMPORTANT: DO NOT ADD NEW VALUES TO THIS ENUM UNLESS THEY ARE AT THE END. +// +//go:generate go run golang.org/x/tools/cmd/stringer -type=ActiveQuoteResponseStatus +type ActiveQuoteResponseStatus uint8 + +const ( + // Considered means the quote request was considered by the relayer, but was not ultimately the fulfilling response. + Considered ActiveQuoteResponseStatus = iota + 1 + // Returned means the quote request was returned by the relayer to the user. + Returned + // PastExpiration means the quote request was received, but past the expiration window. + PastExpiration + // Malformed means that the quote request was malformed. + Malformed + // Duplicate means that the quote request was a duplicate. + Duplicate +) + +// Int returns the int value of the quote request status. +func (q ActiveQuoteResponseStatus) Int() uint8 { + return uint8(q) +} + +// GormDataType implements the gorm common interface for enums. +func (q ActiveQuoteResponseStatus) GormDataType() string { + return dbcommon.EnumDataType +} + +// Scan implements the gorm common interface for enums. +func (q *ActiveQuoteResponseStatus) Scan(src any) error { + res, err := dbcommon.EnumScan(src) + if err != nil { + return fmt.Errorf("could not scan %w", err) + } + newStatus := ActiveQuoteResponseStatus(res) + *q = newStatus + return nil +} + +// Value implements the gorm common interface for enums. +func (q ActiveQuoteResponseStatus) Value() (driver.Value, error) { + // nolint: wrapcheck + return dbcommon.EnumValue(q) +} + +var _ dbcommon.Enum = (*ActiveQuoteResponseStatus)(nil) + +// ActiveQuoteRequest is the database model for an active quote request. +type ActiveQuoteRequest struct { + RequestID string `gorm:"column:request_id;primaryKey"` + IntegratorID string `gorm:"column:integrator_id"` + UserAddress string `gorm:"column:user_address"` + OriginChainID uint64 `gorm:"column:origin_chain_id"` + OriginTokenAddr string `gorm:"column:origin_token"` + DestChainID uint64 `gorm:"column:dest_chain_id"` + DestTokenAddr string `gorm:"column:dest_token"` + OriginAmount decimal.Decimal `gorm:"column:origin_amount"` + ExpirationWindow time.Duration `gorm:"column:expiration_window"` + CreatedAt time.Time `gorm:"column:created_at"` + Status ActiveQuoteRequestStatus `gorm:"column:status"` + ClosedAt *time.Time `gorm:"column:closed_at"` + ClosedQuoteID *string `gorm:"column:closed_quote_id"` +} + +// FromUserRequest converts a model.PutRFQRequest to an ActiveQuoteRequest. +func FromUserRequest(req *model.PutRFQRequest, requestID string) (*ActiveQuoteRequest, error) { + originAmount, err := decimal.NewFromString(req.Data.OriginAmount) + if err != nil { + return nil, fmt.Errorf("invalid origin amount: %w", err) + } + return &ActiveQuoteRequest{ + RequestID: requestID, + IntegratorID: req.IntegratorID, + UserAddress: req.UserAddress, + OriginChainID: uint64(req.Data.OriginChainID), + OriginTokenAddr: req.Data.OriginTokenAddr, + DestChainID: uint64(req.Data.DestChainID), + DestTokenAddr: req.Data.DestTokenAddr, + OriginAmount: originAmount, + ExpirationWindow: time.Duration(req.Data.ExpirationWindow), + CreatedAt: time.Now(), + Status: Received, + }, nil +} + +// ActiveQuoteResponse is the database model for an active quote response. +type ActiveQuoteResponse struct { + RequestID string `gorm:"column:request_id"` + QuoteID string `gorm:"column:quote_id;primaryKey"` + DestAmount decimal.Decimal `gorm:"column:dest_amount"` + RelayerAddr string `gorm:"column:relayer_address"` + UpdatedAt time.Time `gorm:"column:updated_at"` + Status ActiveQuoteResponseStatus `gorm:"column:status"` +} + +// FromRelayerResponse converts a model.WsRFQResponse to an ActiveQuoteResponse. +func FromRelayerResponse(resp *model.WsRFQResponse, relayerAddr string, status ActiveQuoteResponseStatus) (*ActiveQuoteResponse, error) { + destAmount, err := decimal.NewFromString(resp.DestAmount) + if err != nil { + return nil, fmt.Errorf("invalid dest amount: %w", err) + } + return &ActiveQuoteResponse{ + RequestID: resp.RequestID, + QuoteID: resp.QuoteID, + DestAmount: destAmount, + RelayerAddr: relayerAddr, + UpdatedAt: resp.UpdatedAt, + Status: status, + }, nil +} + // APIDBReader is the interface for reading from the database. type APIDBReader interface { // GetQuotesByDestChainAndToken gets quotes from the database by destination chain and token. @@ -42,6 +212,8 @@ type APIDBReader interface { GetQuotesByOriginAndDestination(ctx context.Context, originChainID uint64, originTokenAddr string, destChainID uint64, destTokenAddr string) ([]*Quote, error) // GetQuotesByRelayerAddress gets quotes from the database by relayer address. GetQuotesByRelayerAddress(ctx context.Context, relayerAddress string) ([]*Quote, error) + // GetActiveQuoteRequests gets active quote requests from the database. + GetActiveQuoteRequests(ctx context.Context, matchStatuses ...ActiveQuoteRequestStatus) ([]*ActiveQuoteRequest, error) // GetAllQuotes retrieves all quotes from the database. GetAllQuotes(ctx context.Context) ([]*Quote, error) } @@ -52,6 +224,14 @@ type APIDBWriter interface { UpsertQuote(ctx context.Context, quote *Quote) error // UpsertQuotes upserts multiple quotes in the database. UpsertQuotes(ctx context.Context, quotes []*Quote) error + // InsertActiveQuoteRequest inserts an active quote request into the database. + InsertActiveQuoteRequest(ctx context.Context, req *model.PutRFQRequest, requestID string) error + // UpdateActiveQuoteRequestStatus updates the status of an active quote request in the database. + UpdateActiveQuoteRequestStatus(ctx context.Context, requestID string, quoteID *string, status ActiveQuoteRequestStatus) error + // InsertActiveQuoteResponse inserts an active quote response into the database. + InsertActiveQuoteResponse(ctx context.Context, resp *model.WsRFQResponse, relayerAddr string, status ActiveQuoteResponseStatus) error + // UpdateActiveQuoteResponseStatus updates the status of an active quote response in the database. + UpdateActiveQuoteResponseStatus(ctx context.Context, quoteID string, status ActiveQuoteResponseStatus) error } // APIDB is the interface for the database service. diff --git a/services/rfq/api/db/sql/base/base.go b/services/rfq/api/db/sql/base/base.go index 34eddbc5ca..a2cca36a78 100644 --- a/services/rfq/api/db/sql/base/base.go +++ b/services/rfq/api/db/sql/base/base.go @@ -25,7 +25,7 @@ func (s Store) DB() *gorm.DB { // GetAllModels gets all models to migrate. // see: https://medium.com/@SaifAbid/slice-interfaces-8c78f8b6345d for an explanation of why we can't do this at initialization time func GetAllModels() (allModels []interface{}) { - allModels = append(allModels, &db.Quote{}) + allModels = append(allModels, &db.Quote{}, &db.ActiveQuoteRequest{}, &db.ActiveQuoteResponse{}) return allModels } diff --git a/services/rfq/api/db/sql/base/store.go b/services/rfq/api/db/sql/base/store.go index 8ef37607e8..312ec15472 100644 --- a/services/rfq/api/db/sql/base/store.go +++ b/services/rfq/api/db/sql/base/store.go @@ -3,10 +3,12 @@ package base import ( "context" "fmt" + "time" "gorm.io/gorm/clause" "github.com/synapsecns/sanguine/services/rfq/api/db" + "github.com/synapsecns/sanguine/services/rfq/api/model" ) // GetQuotesByDestChainAndToken gets quotes from the database by destination chain and token. @@ -77,3 +79,78 @@ func (s *Store) UpsertQuotes(ctx context.Context, quotes []*db.Quote) error { } return nil } + +// InsertActiveQuoteRequest inserts an active quote request into the database. +func (s *Store) InsertActiveQuoteRequest(ctx context.Context, req *model.PutRFQRequest, requestID string) error { + dbReq, err := db.FromUserRequest(req, requestID) + if err != nil { + return fmt.Errorf("could not convert user request to database request: %w", err) + } + result := s.db.WithContext(ctx).Create(dbReq) + if result.Error != nil { + return fmt.Errorf("could not insert active quote request: %w", result.Error) + } + return nil +} + +// UpdateActiveQuoteRequestStatus updates the status of an active quote request in the database. +func (s *Store) UpdateActiveQuoteRequestStatus(ctx context.Context, requestID string, quoteID *string, status db.ActiveQuoteRequestStatus) error { + updates := map[string]interface{}{ + "status": status, + } + if status == db.Closed { + if quoteID == nil { + return fmt.Errorf("quote id is required for fulfilled status") + } + updates["closed_quote_id"] = quoteID + updates["closed_at"] = time.Now().UTC() + } + result := s.db.WithContext(ctx). + Model(&db.ActiveQuoteRequest{}). + Where("request_id = ?", requestID). + Updates(updates) + if result.Error != nil { + return fmt.Errorf("could not update active quote request status: %w", result.Error) + } + return nil +} + +// InsertActiveQuoteResponse inserts an active quote response into the database. +func (s *Store) InsertActiveQuoteResponse(ctx context.Context, resp *model.WsRFQResponse, relayerAddr string, status db.ActiveQuoteResponseStatus) error { + dbReq, err := db.FromRelayerResponse(resp, relayerAddr, status) + if err != nil { + return fmt.Errorf("could not convert relayer response to database response: %w", err) + } + result := s.db.WithContext(ctx).Create(dbReq) + if result.Error != nil { + return fmt.Errorf("could not insert active quote response: %w", result.Error) + } + return nil +} + +// UpdateActiveQuoteResponseStatus updates the status of an active quote response in the database. +func (s *Store) UpdateActiveQuoteResponseStatus(ctx context.Context, quoteID string, status db.ActiveQuoteResponseStatus) error { + result := s.db.WithContext(ctx). + Model(&db.ActiveQuoteResponse{}). + Where("quote_id = ?", quoteID). + Update("status", status) + if result.Error != nil { + return fmt.Errorf("could not update active quote response status: %w", result.Error) + } + return nil +} + +// GetActiveQuoteRequests gets active quote requests from the database. +func (s *Store) GetActiveQuoteRequests(ctx context.Context, matchStatuses ...db.ActiveQuoteRequestStatus) ([]*db.ActiveQuoteRequest, error) { + var requests []*db.ActiveQuoteRequest + + query := s.db.WithContext(ctx).Model(&db.ActiveQuoteRequest{}) + if len(matchStatuses) > 0 { + query = query.Where("status IN ?", matchStatuses) + } + result := query.Find(&requests) + if result.Error != nil { + return nil, result.Error + } + return requests, nil +} diff --git a/services/rfq/api/docs/docs.go b/services/rfq/api/docs/docs.go index af06bfa449..447b332d29 100644 --- a/services/rfq/api/docs/docs.go +++ b/services/rfq/api/docs/docs.go @@ -35,7 +35,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.PutQuoteRequest" + "$ref": "#/definitions/model.PutRelayerQuoteRequest" } } ], @@ -121,6 +121,38 @@ const docTemplate = `{ } } }, + "/open_quote_requests": { + "get": { + "description": "Get all open quote requests that are currently in Received or Pending status.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "quotes" + ], + "summary": "Get open quote requests", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.GetOpenQuoteRequestsResponse" + } + }, + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } + } + } + } + } + }, "/quotes": { "get": { "description": "get quotes from all relayers.", @@ -203,7 +235,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.PutQuoteRequest" + "$ref": "#/definitions/model.PutRelayerQuoteRequest" } } ], @@ -219,6 +251,72 @@ const docTemplate = `{ } } } + }, + "/rfq": { + "put": { + "description": "Handle user quote request and return the best quote available.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "quotes" + ], + "summary": "Handle user quote request", + "parameters": [ + { + "description": "User quote request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.PutRFQRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.PutRFQResponse" + }, + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } + } + } + } + } + }, + "/rfq_stream": { + "get": { + "description": "Establish a WebSocket connection to receive active quote requests.", + "produces": [ + "application/json" + ], + "tags": [ + "quotes" + ], + "summary": "Handle WebSocket connection for active quote requests", + "responses": { + "101": { + "description": "Switching Protocols", + "schema": { + "type": "string" + }, + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } + } + } + } + } } }, "definitions": { @@ -234,6 +332,35 @@ const docTemplate = `{ } } }, + "model.GetOpenQuoteRequestsResponse": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "dest_chain_id": { + "type": "integer" + }, + "dest_token": { + "type": "string" + }, + "expiration_window": { + "type": "integer" + }, + "origin_amount": { + "type": "string" + }, + "origin_chain_id": { + "type": "integer" + }, + "origin_token": { + "type": "string" + }, + "user_address": { + "type": "string" + } + } + }, "model.GetQuoteResponse": { "type": "object", "properties": { @@ -289,12 +416,55 @@ const docTemplate = `{ "quotes": { "type": "array", "items": { - "$ref": "#/definitions/model.PutQuoteRequest" + "$ref": "#/definitions/model.PutRelayerQuoteRequest" + } + } + } + }, + "model.PutRFQRequest": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.QuoteData" + }, + "integrator_id": { + "type": "string" + }, + "quote_types": { + "type": "array", + "items": { + "type": "string" } + }, + "user_address": { + "type": "string" + } + } + }, + "model.PutRFQResponse": { + "type": "object", + "properties": { + "dest_amount": { + "type": "string" + }, + "quote_id": { + "type": "string" + }, + "quote_type": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "relayer_address": { + "type": "string" + }, + "success": { + "type": "boolean" } } }, - "model.PutQuoteRequest": { + "model.PutRelayerQuoteRequest": { "type": "object", "properties": { "dest_amount": { @@ -325,6 +495,38 @@ const docTemplate = `{ "type": "string" } } + }, + "model.QuoteData": { + "type": "object", + "properties": { + "dest_amount": { + "type": "string" + }, + "dest_chain_id": { + "type": "integer" + }, + "dest_token_addr": { + "type": "string" + }, + "expiration_window": { + "type": "integer" + }, + "origin_amount": { + "type": "string" + }, + "origin_chain_id": { + "type": "integer" + }, + "origin_token_addr": { + "type": "string" + }, + "quote_id": { + "type": "string" + }, + "relayer_address": { + "type": "string" + } + } } } }` diff --git a/services/rfq/api/docs/swagger.json b/services/rfq/api/docs/swagger.json index 5d28bbf785..0357cd7e31 100644 --- a/services/rfq/api/docs/swagger.json +++ b/services/rfq/api/docs/swagger.json @@ -24,7 +24,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.PutQuoteRequest" + "$ref": "#/definitions/model.PutRelayerQuoteRequest" } } ], @@ -110,6 +110,38 @@ } } }, + "/open_quote_requests": { + "get": { + "description": "Get all open quote requests that are currently in Received or Pending status.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "quotes" + ], + "summary": "Get open quote requests", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/model.GetOpenQuoteRequestsResponse" + } + }, + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } + } + } + } + } + }, "/quotes": { "get": { "description": "get quotes from all relayers.", @@ -192,7 +224,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/model.PutQuoteRequest" + "$ref": "#/definitions/model.PutRelayerQuoteRequest" } } ], @@ -208,6 +240,72 @@ } } } + }, + "/rfq": { + "put": { + "description": "Handle user quote request and return the best quote available.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "quotes" + ], + "summary": "Handle user quote request", + "parameters": [ + { + "description": "User quote request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/model.PutRFQRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/model.PutRFQResponse" + }, + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } + } + } + } + } + }, + "/rfq_stream": { + "get": { + "description": "Establish a WebSocket connection to receive active quote requests.", + "produces": [ + "application/json" + ], + "tags": [ + "quotes" + ], + "summary": "Handle WebSocket connection for active quote requests", + "responses": { + "101": { + "description": "Switching Protocols", + "schema": { + "type": "string" + }, + "headers": { + "X-Api-Version": { + "type": "string", + "description": "API Version Number - See docs for more info" + } + } + } + } + } } }, "definitions": { @@ -223,6 +321,35 @@ } } }, + "model.GetOpenQuoteRequestsResponse": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "dest_chain_id": { + "type": "integer" + }, + "dest_token": { + "type": "string" + }, + "expiration_window": { + "type": "integer" + }, + "origin_amount": { + "type": "string" + }, + "origin_chain_id": { + "type": "integer" + }, + "origin_token": { + "type": "string" + }, + "user_address": { + "type": "string" + } + } + }, "model.GetQuoteResponse": { "type": "object", "properties": { @@ -278,12 +405,55 @@ "quotes": { "type": "array", "items": { - "$ref": "#/definitions/model.PutQuoteRequest" + "$ref": "#/definitions/model.PutRelayerQuoteRequest" + } + } + } + }, + "model.PutRFQRequest": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.QuoteData" + }, + "integrator_id": { + "type": "string" + }, + "quote_types": { + "type": "array", + "items": { + "type": "string" } + }, + "user_address": { + "type": "string" + } + } + }, + "model.PutRFQResponse": { + "type": "object", + "properties": { + "dest_amount": { + "type": "string" + }, + "quote_id": { + "type": "string" + }, + "quote_type": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "relayer_address": { + "type": "string" + }, + "success": { + "type": "boolean" } } }, - "model.PutQuoteRequest": { + "model.PutRelayerQuoteRequest": { "type": "object", "properties": { "dest_amount": { @@ -314,6 +484,38 @@ "type": "string" } } + }, + "model.QuoteData": { + "type": "object", + "properties": { + "dest_amount": { + "type": "string" + }, + "dest_chain_id": { + "type": "integer" + }, + "dest_token_addr": { + "type": "string" + }, + "expiration_window": { + "type": "integer" + }, + "origin_amount": { + "type": "string" + }, + "origin_chain_id": { + "type": "integer" + }, + "origin_token_addr": { + "type": "string" + }, + "quote_id": { + "type": "string" + }, + "relayer_address": { + "type": "string" + } + } } } } \ No newline at end of file diff --git a/services/rfq/api/docs/swagger.yaml b/services/rfq/api/docs/swagger.yaml index a8ddffdcc6..8e6bc56240 100644 --- a/services/rfq/api/docs/swagger.yaml +++ b/services/rfq/api/docs/swagger.yaml @@ -7,6 +7,25 @@ definitions: description: Contracts is a map of chain id to contract address type: object type: object + model.GetOpenQuoteRequestsResponse: + properties: + created_at: + type: string + dest_chain_id: + type: integer + dest_token: + type: string + expiration_window: + type: integer + origin_amount: + type: string + origin_chain_id: + type: integer + origin_token: + type: string + user_address: + type: string + type: object model.GetQuoteResponse: properties: dest_amount: @@ -55,10 +74,38 @@ definitions: properties: quotes: items: - $ref: '#/definitions/model.PutQuoteRequest' + $ref: '#/definitions/model.PutRelayerQuoteRequest' type: array type: object - model.PutQuoteRequest: + model.PutRFQRequest: + properties: + data: + $ref: '#/definitions/model.QuoteData' + integrator_id: + type: string + quote_types: + items: + type: string + type: array + user_address: + type: string + type: object + model.PutRFQResponse: + properties: + dest_amount: + type: string + quote_id: + type: string + quote_type: + type: string + reason: + type: string + relayer_address: + type: string + success: + type: boolean + type: object + model.PutRelayerQuoteRequest: properties: dest_amount: type: string @@ -79,6 +126,27 @@ definitions: origin_token_addr: type: string type: object + model.QuoteData: + properties: + dest_amount: + type: string + dest_chain_id: + type: integer + dest_token_addr: + type: string + expiration_window: + type: integer + origin_amount: + type: string + origin_chain_id: + type: integer + origin_token_addr: + type: string + quote_id: + type: string + relayer_address: + type: string + type: object info: contact: {} paths: @@ -93,7 +161,7 @@ paths: name: request required: true schema: - $ref: '#/definitions/model.PutQuoteRequest' + $ref: '#/definitions/model.PutRelayerQuoteRequest' produces: - application/json responses: @@ -151,6 +219,28 @@ paths: summary: Get contract addresses tags: - quotes + /open_quote_requests: + get: + consumes: + - application/json + description: Get all open quote requests that are currently in Received or Pending + status. + produces: + - application/json + responses: + "200": + description: OK + headers: + X-Api-Version: + description: API Version Number - See docs for more info + type: string + schema: + items: + $ref: '#/definitions/model.GetOpenQuoteRequestsResponse' + type: array + summary: Get open quote requests + tags: + - quotes /quotes: get: consumes: @@ -203,7 +293,7 @@ paths: name: request required: true schema: - $ref: '#/definitions/model.PutQuoteRequest' + $ref: '#/definitions/model.PutRelayerQuoteRequest' produces: - application/json responses: @@ -216,4 +306,47 @@ paths: summary: Upsert quote tags: - quotes + /rfq: + put: + consumes: + - application/json + description: Handle user quote request and return the best quote available. + parameters: + - description: User quote request + in: body + name: request + required: true + schema: + $ref: '#/definitions/model.PutRFQRequest' + produces: + - application/json + responses: + "200": + description: OK + headers: + X-Api-Version: + description: API Version Number - See docs for more info + type: string + schema: + $ref: '#/definitions/model.PutRFQResponse' + summary: Handle user quote request + tags: + - quotes + /rfq_stream: + get: + description: Establish a WebSocket connection to receive active quote requests. + produces: + - application/json + responses: + "101": + description: Switching Protocols + headers: + X-Api-Version: + description: API Version Number - See docs for more info + type: string + schema: + type: string + summary: Handle WebSocket connection for active quote requests + tags: + - quotes swagger: "2.0" diff --git a/services/rfq/api/model/request.go b/services/rfq/api/model/request.go index cff2db2161..c0dd864068 100644 --- a/services/rfq/api/model/request.go +++ b/services/rfq/api/model/request.go @@ -1,7 +1,9 @@ package model -// PutQuoteRequest contains the schema for a PUT /quote request. -type PutQuoteRequest struct { +import "time" + +// PutRelayerQuoteRequest contains the schema for a PUT /quote request. +type PutRelayerQuoteRequest struct { OriginChainID int `json:"origin_chain_id"` OriginTokenAddr string `json:"origin_token_addr"` DestChainID int `json:"dest_chain_id"` @@ -15,7 +17,7 @@ type PutQuoteRequest struct { // PutBulkQuotesRequest contains the schema for a PUT /quote request. type PutBulkQuotesRequest struct { - Quotes []PutQuoteRequest `json:"quotes"` + Quotes []PutRelayerQuoteRequest `json:"quotes"` } // PutAckRequest contains the schema for a PUT /ack request. @@ -31,3 +33,54 @@ type GetQuoteSpecificRequest struct { DestChainID int `json:"destChainId"` DestTokenAddr string `json:"destTokenAddr"` } + +// PutRFQRequest represents a user request for quote. +type PutRFQRequest struct { + UserAddress string `json:"user_address"` + IntegratorID string `json:"integrator_id"` + QuoteTypes []string `json:"quote_types"` + Data QuoteData `json:"data"` +} + +// QuoteRequest represents a request for a quote. +type QuoteRequest struct { + RequestID string `json:"request_id"` + Data QuoteData `json:"data"` + CreatedAt time.Time `json:"created_at"` +} + +// QuoteData represents the data within a quote request. +type QuoteData struct { + OriginChainID int `json:"origin_chain_id"` + DestChainID int `json:"dest_chain_id"` + OriginTokenAddr string `json:"origin_token_addr"` + DestTokenAddr string `json:"dest_token_addr"` + OriginAmount string `json:"origin_amount"` + ExpirationWindow int64 `json:"expiration_window"` + DestAmount *string `json:"dest_amount"` + RelayerAddress *string `json:"relayer_address"` + QuoteID *string `json:"quote_id"` +} + +// WsRFQRequest represents a request for a quote to a relayer. +type WsRFQRequest struct { + RequestID string `json:"request_id"` + Data QuoteData `json:"data"` + CreatedAt time.Time `json:"created_at"` +} + +// SubscribeActiveRFQRequest represents a request to subscribe to active quotes. +// Note that this request is not actually bound to the request body, but rather the chain IDs +// are encoded under the ChainsHeader. +type SubscribeActiveRFQRequest struct { + ChainIDs []int `json:"chain_ids"` +} + +// NewWsRFQRequest creates a new WsRFQRequest. +func NewWsRFQRequest(data QuoteData, requestID string) *WsRFQRequest { + return &WsRFQRequest{ + RequestID: requestID, + Data: data, + CreatedAt: time.Now(), + } +} diff --git a/services/rfq/api/model/response.go b/services/rfq/api/model/response.go index 6cfd2a1599..72c534e91f 100644 --- a/services/rfq/api/model/response.go +++ b/services/rfq/api/model/response.go @@ -1,5 +1,10 @@ package model +import ( + "encoding/json" + "time" +) + // GetQuoteResponse contains the schema for a GET /quote response. type GetQuoteResponse struct { // OriginChainID is the chain which the relayer is willing to relay from @@ -41,3 +46,45 @@ type GetContractsResponse struct { // Contracts is a map of chain id to contract address Contracts map[uint32]string `json:"contracts"` } + +// ActiveRFQMessage represents the general structure of WebSocket messages for Active RFQ. +type ActiveRFQMessage struct { + Op string `json:"op"` + Content json.RawMessage `json:"content,omitempty"` + Success bool `json:"success,omitempty"` +} + +// PutRFQResponse represents a response to a user quote request. +type PutRFQResponse struct { + Success bool `json:"success"` + Reason string `json:"reason,omitempty"` + QuoteType string `json:"quote_type,omitempty"` + QuoteID *string `json:"quote_id,omitempty"` + DestAmount string `json:"dest_amount,omitempty"` + RelayerAddress string `json:"relayer_address,omitempty"` +} + +// WsRFQResponse represents a response to a quote request. +type WsRFQResponse struct { + RequestID string `json:"request_id"` + QuoteID string `json:"quote_id,omitempty"` + DestAmount string `json:"dest_amount"` + UpdatedAt time.Time `json:"updated_at"` +} + +// SubscriptionParams are the parameters for a subscription. +type SubscriptionParams struct { + Chains []int `json:"chains"` +} + +// GetOpenQuoteRequestsResponse represents a response to a GET /open_quote_requests request. +type GetOpenQuoteRequestsResponse struct { + UserAddress string `json:"user_address"` + OriginChainID uint64 `json:"origin_chain_id"` + OriginTokenAddr string `json:"origin_token"` + DestChainID uint64 `json:"dest_chain_id"` + DestTokenAddr string `json:"dest_token"` + OriginAmount string `json:"origin_amount"` + ExpirationWindow int `json:"expiration_window"` + CreatedAt time.Time `json:"created_at"` +} diff --git a/services/rfq/api/model/util.go b/services/rfq/api/model/util.go deleted file mode 100644 index 3f35f7b14f..0000000000 --- a/services/rfq/api/model/util.go +++ /dev/null @@ -1,24 +0,0 @@ -package model - -import ( - "time" - - "github.com/synapsecns/sanguine/services/rfq/api/db" -) - -// QuoteResponseFromDbQuote converts a db.Quote to a GetQuoteResponse. -func QuoteResponseFromDbQuote(dbQuote *db.Quote) *GetQuoteResponse { - return &GetQuoteResponse{ - OriginChainID: int(dbQuote.OriginChainID), - OriginTokenAddr: dbQuote.OriginTokenAddr, - DestChainID: int(dbQuote.DestChainID), - DestTokenAddr: dbQuote.DestTokenAddr, - DestAmount: dbQuote.DestAmount.String(), - MaxOriginAmount: dbQuote.MaxOriginAmount.String(), - FixedFee: dbQuote.FixedFee.String(), - RelayerAddr: dbQuote.RelayerAddr, - OriginFastBridgeAddress: dbQuote.OriginFastBridgeAddress, - DestFastBridgeAddress: dbQuote.DestFastBridgeAddress, - UpdatedAt: dbQuote.UpdatedAt.Format(time.RFC3339), - } -} diff --git a/services/rfq/api/rest/auth.go b/services/rfq/api/rest/auth.go index 0c407b8de3..e8d5f24bc6 100644 --- a/services/rfq/api/rest/auth.go +++ b/services/rfq/api/rest/auth.go @@ -20,7 +20,7 @@ import ( // so that full auth header string: auth = strconv.Itoa(time.Now().Unix()) + ":" + signature // see: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign func EIP191Auth(c *gin.Context, deadline int64) (accountRecovered common.Address, err error) { - auth := c.Request.Header.Get("Authorization") + auth := c.Request.Header.Get(AuthorizationHeader) // parse : s := strings.Split(auth, ":") diff --git a/services/rfq/api/rest/handler.go b/services/rfq/api/rest/handler.go index 2878fb4cb3..c0aa5910e8 100644 --- a/services/rfq/api/rest/handler.go +++ b/services/rfq/api/rest/handler.go @@ -44,7 +44,7 @@ func APIVersionMiddleware(serverVersion string) gin.HandlerFunc { // @Summary Upsert quote // @Schemes // @Description upsert a quote from relayer. -// @Param request body model.PutQuoteRequest true "query params" +// @Param request body model.PutRelayerQuoteRequest true "query params" // @Tags quotes // @Accept json // @Produce json @@ -63,7 +63,7 @@ func (h *Handler) ModifyQuote(c *gin.Context) { c.JSON(http.StatusBadRequest, gin.H{"error": "No relayer address recovered from signature"}) return } - putRequest, ok := req.(*model.PutQuoteRequest) + putRequest, ok := req.(*model.PutRelayerQuoteRequest) if !ok { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request type"}) return @@ -133,7 +133,8 @@ func (h *Handler) ModifyBulkQuotes(c *gin.Context) { c.Status(http.StatusOK) } -func parseDBQuote(putRequest model.PutQuoteRequest, relayerAddr interface{}) (*db.Quote, error) { +//nolint:gosec +func parseDBQuote(putRequest model.PutRelayerQuoteRequest, relayerAddr interface{}) (*db.Quote, error) { destAmount, err := decimal.NewFromString(putRequest.DestAmount) if err != nil { return nil, fmt.Errorf("invalid DestAmount") @@ -162,6 +163,23 @@ func parseDBQuote(putRequest model.PutQuoteRequest, relayerAddr interface{}) (*d }, nil } +//nolint:gosec +func quoteResponseFromDBQuote(dbQuote *db.Quote) *model.GetQuoteResponse { + return &model.GetQuoteResponse{ + OriginChainID: int(dbQuote.OriginChainID), + OriginTokenAddr: dbQuote.OriginTokenAddr, + DestChainID: int(dbQuote.DestChainID), + DestTokenAddr: dbQuote.DestTokenAddr, + DestAmount: dbQuote.DestAmount.String(), + MaxOriginAmount: dbQuote.MaxOriginAmount.String(), + FixedFee: dbQuote.FixedFee.String(), + RelayerAddr: dbQuote.RelayerAddr, + OriginFastBridgeAddress: dbQuote.OriginFastBridgeAddress, + DestFastBridgeAddress: dbQuote.DestFastBridgeAddress, + UpdatedAt: dbQuote.UpdatedAt.Format(time.RFC3339), + } +} + // GetQuotes retrieves all quotes from the database. // GET /quotes. // nolint: cyclop @@ -229,11 +247,48 @@ func (h *Handler) GetQuotes(c *gin.Context) { // Convert quotes from db model to api model quotes := make([]*model.GetQuoteResponse, len(dbQuotes)) for i, dbQuote := range dbQuotes { - quotes[i] = model.QuoteResponseFromDbQuote(dbQuote) + quotes[i] = quoteResponseFromDBQuote(dbQuote) + } + c.JSON(http.StatusOK, quotes) +} + +// GetOpenQuoteRequests retrieves all open quote requests. +// GET /open_quote_requests +// @Summary Get open quote requests +// @Description Get all open quote requests that are currently in Received or Pending status. +// @Tags quotes +// @Accept json +// @Produce json +// @Success 200 {array} model.GetOpenQuoteRequestsResponse +// @Header 200 {string} X-Api-Version "API Version Number - See docs for more info" +// @Router /open_quote_requests [get]. +func (h *Handler) GetOpenQuoteRequests(c *gin.Context) { + dbQuotes, err := h.db.GetActiveQuoteRequests(c, db.Received, db.Pending) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + quotes := make([]*model.GetOpenQuoteRequestsResponse, len(dbQuotes)) + for i, dbQuote := range dbQuotes { + quotes[i] = dbActiveQuoteRequestToModel(dbQuote) } c.JSON(http.StatusOK, quotes) } +func dbActiveQuoteRequestToModel(dbQuote *db.ActiveQuoteRequest) *model.GetOpenQuoteRequestsResponse { + return &model.GetOpenQuoteRequestsResponse{ + UserAddress: dbQuote.UserAddress, + OriginChainID: dbQuote.OriginChainID, + OriginTokenAddr: dbQuote.OriginTokenAddr, + DestChainID: dbQuote.DestChainID, + DestTokenAddr: dbQuote.DestTokenAddr, + OriginAmount: dbQuote.OriginAmount.String(), + ExpirationWindow: int(dbQuote.ExpirationWindow.Milliseconds()), + CreatedAt: dbQuote.CreatedAt, + } +} + // GetContracts retrieves all contracts api is currently enabled on. // GET /contracts. // PingExample godoc diff --git a/services/rfq/api/rest/pubsub.go b/services/rfq/api/rest/pubsub.go new file mode 100644 index 0000000000..b92987da17 --- /dev/null +++ b/services/rfq/api/rest/pubsub.go @@ -0,0 +1,80 @@ +package rest + +import ( + "fmt" + + "github.com/puzpuzpuz/xsync" + "github.com/synapsecns/sanguine/services/rfq/api/model" +) + +// PubSubManager is a manager for a pubsub system. +type PubSubManager interface { + AddSubscription(relayerAddr string, params model.SubscriptionParams) error + RemoveSubscription(relayerAddr string, params model.SubscriptionParams) error + IsSubscribed(relayerAddr string, origin, dest int) bool +} + +type pubSubManagerImpl struct { + subscriptions *xsync.MapOf[string, map[int]struct{}] +} + +// NewPubSubManager creates a new pubsub manager. +func NewPubSubManager() PubSubManager { + return &pubSubManagerImpl{ + subscriptions: xsync.NewMapOf[map[int]struct{}](), + } +} + +func (p *pubSubManagerImpl) AddSubscription(relayerAddr string, params model.SubscriptionParams) error { + if params.Chains == nil { + return fmt.Errorf("chains is nil") + } + + sub, ok := p.subscriptions.Load(relayerAddr) + if !ok { + sub = make(map[int]struct{}) + for _, c := range params.Chains { + sub[c] = struct{}{} + } + p.subscriptions.Store(relayerAddr, sub) + return nil + } + for _, c := range params.Chains { + sub[c] = struct{}{} + } + return nil +} + +func (p *pubSubManagerImpl) RemoveSubscription(relayerAddr string, params model.SubscriptionParams) error { + if params.Chains == nil { + return fmt.Errorf("chains is nil") + } + + sub, ok := p.subscriptions.Load(relayerAddr) + if !ok { + return fmt.Errorf("relayer %s has no subscriptions", relayerAddr) + } + + for _, c := range params.Chains { + _, ok := sub[c] + if !ok { + return fmt.Errorf("relayer %s is not subscribed to chain %d", relayerAddr, c) + } + delete(sub, c) + } + + return nil +} + +func (p *pubSubManagerImpl) IsSubscribed(relayerAddr string, origin, dest int) bool { + sub, ok := p.subscriptions.Load(relayerAddr) + if !ok { + return false + } + _, ok = sub[origin] + if !ok { + return false + } + _, ok = sub[dest] + return ok +} diff --git a/services/rfq/api/rest/rfq.go b/services/rfq/api/rest/rfq.go new file mode 100644 index 0000000000..770732c887 --- /dev/null +++ b/services/rfq/api/rest/rfq.go @@ -0,0 +1,272 @@ +package rest + +import ( + "context" + "errors" + "fmt" + "math/big" + "sync" + "time" + + "github.com/google/uuid" + "github.com/synapsecns/sanguine/core/metrics" + "github.com/synapsecns/sanguine/services/rfq/api/db" + "github.com/synapsecns/sanguine/services/rfq/api/model" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) + +const collectionTimeout = 1 * time.Minute + +func (r *QuoterAPIServer) handleActiveRFQ(ctx context.Context, request *model.PutRFQRequest, requestID string) (quote *model.QuoteData) { + ctx, span := r.handler.Tracer().Start(ctx, "handleActiveRFQ", trace.WithAttributes( + attribute.String("user_address", request.UserAddress), + attribute.String("request_id", requestID), + )) + defer func() { + metrics.EndSpan(span) + }() + + // publish the quote request to all connected clients + relayerReq := model.NewWsRFQRequest(request.Data, requestID) + r.wsClients.Range(func(relayerAddr string, client WsClient) bool { + sendCtx, sendSpan := r.handler.Tracer().Start(ctx, "sendQuoteRequest", trace.WithAttributes( + attribute.String("relayer_address", relayerAddr), + attribute.String("request_id", requestID), + )) + defer metrics.EndSpan(sendSpan) + + subscribed := r.pubSubManager.IsSubscribed(relayerAddr, request.Data.OriginChainID, request.Data.DestChainID) + span.SetAttributes(attribute.Bool("subscribed", subscribed)) + if subscribed { + err := client.SendQuoteRequest(sendCtx, relayerReq) + if err != nil { + logger.Errorf("Error sending quote request to %s: %v", relayerAddr, err) + } + } + return true + }) + err := r.db.UpdateActiveQuoteRequestStatus(ctx, requestID, nil, db.Pending) + if err != nil { + logger.Errorf("Error updating active quote request status: %v", err) + } + + // collect the responses and determine the best quote + responses := r.collectRelayerResponses(ctx, request, requestID) + for r, resp := range responses { + relayerAddr := r + quote = getBestQuote(quote, getRelayerQuoteData(request, resp)) + quote.RelayerAddress = &relayerAddr + } + err = r.recordActiveQuote(ctx, quote, requestID) + if err != nil { + logger.Errorf("Error recording active quote: %v", err) + } + + return quote +} + +func (r *QuoterAPIServer) collectRelayerResponses(ctx context.Context, request *model.PutRFQRequest, requestID string) (responses map[string]*model.WsRFQResponse) { + ctx, span := r.handler.Tracer().Start(ctx, "collectRelayerResponses", trace.WithAttributes( + attribute.String("user_address", request.UserAddress), + attribute.String("request_id", requestID), + )) + defer metrics.EndSpan(span) + + expireCtx, expireCancel := context.WithTimeout(ctx, time.Duration(request.Data.ExpirationWindow)*time.Millisecond) + defer expireCancel() + + // don't cancel the collection context so that late responses can be collected in background + // nolint:govet + collectionCtx, _ := context.WithTimeout(ctx, time.Duration(request.Data.ExpirationWindow)*time.Millisecond+collectionTimeout) + + wg := sync.WaitGroup{} + respMux := sync.Mutex{} + responses = map[string]*model.WsRFQResponse{} + r.wsClients.Range(func(relayerAddr string, client WsClient) bool { + wg.Add(1) + go func(client WsClient) { + var respStatus db.ActiveQuoteResponseStatus + var err error + _, clientSpan := r.handler.Tracer().Start(collectionCtx, "collectRelayerResponses", trace.WithAttributes( + attribute.String("relayer_address", relayerAddr), + attribute.String("request_id", requestID), + )) + defer func() { + clientSpan.SetAttributes(attribute.String("status", respStatus.String())) + metrics.EndSpanWithErr(clientSpan, err) + }() + + defer wg.Done() + resp, err := client.ReceiveQuoteResponse(collectionCtx, requestID) + if err != nil { + logger.Errorf("Error receiving quote response: %v", err) + return + } + clientSpan.AddEvent("received quote response", trace.WithAttributes( + attribute.String("relayer_address", relayerAddr), + attribute.String("request_id", requestID), + attribute.String("dest_amount", resp.DestAmount), + )) + + // validate the response + respStatus = getQuoteResponseStatus(expireCtx, resp) + if respStatus == db.Considered { + respMux.Lock() + responses[relayerAddr] = resp + respMux.Unlock() + } + + // record the response + err = r.db.InsertActiveQuoteResponse(collectionCtx, resp, relayerAddr, respStatus) + if err != nil { + logger.Errorf("Error inserting active quote response: %v", err) + } + }(client) + return true + }) + + // wait for all responses to be received, or expiration + select { + case <-expireCtx.Done(): + // request expired before all responses were received + case <-func() chan struct{} { + ch := make(chan struct{}) + go func() { + wg.Wait() + close(ch) + }() + return ch + }(): + // all responses received + } + + return responses +} + +func getRelayerQuoteData(request *model.PutRFQRequest, resp *model.WsRFQResponse) *model.QuoteData { + return &model.QuoteData{ + OriginChainID: request.Data.OriginChainID, + DestChainID: request.Data.DestChainID, + OriginTokenAddr: request.Data.OriginTokenAddr, + DestTokenAddr: request.Data.DestTokenAddr, + OriginAmount: request.Data.OriginAmount, + DestAmount: &resp.DestAmount, + QuoteID: &resp.QuoteID, + } +} + +func getBestQuote(a, b *model.QuoteData) *model.QuoteData { + if a == nil && b == nil { + return nil + } + if a == nil { + return b + } + if b == nil { + return a + } + aAmount, _ := new(big.Int).SetString(*a.DestAmount, 10) + bAmount, _ := new(big.Int).SetString(*b.DestAmount, 10) + if aAmount.Cmp(bAmount) > 0 { + return a + } + return b +} + +func getQuoteResponseStatus(ctx context.Context, resp *model.WsRFQResponse) db.ActiveQuoteResponseStatus { + respStatus := db.Considered + err := validateRelayerQuoteResponse(resp) + if err != nil { + respStatus = db.Malformed + logger.Errorf("Error validating quote response: %v", err) + } else if ctx.Err() != nil { + respStatus = db.PastExpiration + } + return respStatus +} + +func validateRelayerQuoteResponse(resp *model.WsRFQResponse) error { + _, ok := new(big.Int).SetString(resp.DestAmount, 10) + if !ok { + return fmt.Errorf("dest amount is invalid") + } + // TODO: compute quote ID from request + resp.QuoteID = uuid.New().String() + return nil +} + +func (r *QuoterAPIServer) recordActiveQuote(ctx context.Context, quote *model.QuoteData, requestID string) (err error) { + if quote == nil { + err = r.db.UpdateActiveQuoteRequestStatus(ctx, requestID, nil, db.Expired) + if err != nil { + logger.Errorf("Error updating active quote request status: %v", err) + } + } else { + err = r.db.UpdateActiveQuoteRequestStatus(ctx, requestID, quote.QuoteID, db.Closed) + if err != nil { + logger.Errorf("Error updating active quote request status: %v", err) + } + err = r.db.UpdateActiveQuoteResponseStatus(ctx, *quote.QuoteID, db.Returned) + if err != nil { + return fmt.Errorf("error updating active quote response status: %w", err) + } + } + return nil +} + +func (r *QuoterAPIServer) handlePassiveRFQ(ctx context.Context, request *model.PutRFQRequest) (*model.QuoteData, error) { + ctx, span := r.handler.Tracer().Start(ctx, "handlePassiveRFQ", trace.WithAttributes( + attribute.String("user_address", request.UserAddress), + )) + defer metrics.EndSpan(span) + + quotes, err := r.db.GetQuotesByOriginAndDestination(ctx, uint64(request.Data.OriginChainID), request.Data.OriginTokenAddr, uint64(request.Data.DestChainID), request.Data.DestTokenAddr) + if err != nil { + return nil, fmt.Errorf("failed to get quotes: %w", err) + } + + originAmount, ok := new(big.Int).SetString(request.Data.OriginAmount, 10) + if !ok { + return nil, errors.New("invalid origin amount") + } + + var bestQuote *model.QuoteData + for _, quote := range quotes { + quoteOriginAmount, ok := new(big.Int).SetString(quote.MaxOriginAmount.String(), 10) + if !ok { + continue + } + if quoteOriginAmount.Cmp(originAmount) < 0 { + continue + } + quotePrice := new(big.Float).Quo( + new(big.Float).SetInt(quote.DestAmount.BigInt()), + new(big.Float).SetInt(quote.MaxOriginAmount.BigInt()), + ) + + rawDestAmount := new(big.Float).Mul( + new(big.Float).SetInt(originAmount), + quotePrice, + ) + + rawDestAmountInt, _ := rawDestAmount.Int(nil) + if rawDestAmountInt.Cmp(quote.FixedFee.BigInt()) < 0 { + continue + } + destAmount := new(big.Int).Sub(rawDestAmountInt, quote.FixedFee.BigInt()).String() + //nolint:gosec + quoteData := &model.QuoteData{ + OriginChainID: int(quote.OriginChainID), + DestChainID: int(quote.DestChainID), + OriginTokenAddr: quote.OriginTokenAddr, + DestTokenAddr: quote.DestTokenAddr, + OriginAmount: quote.MaxOriginAmount.String(), + DestAmount: &destAmount, + RelayerAddress: "e.RelayerAddr, + } + bestQuote = getBestQuote(bestQuote, quoteData) + } + + return bestQuote, nil +} diff --git a/services/rfq/api/rest/rfq_test.go b/services/rfq/api/rest/rfq_test.go new file mode 100644 index 0000000000..624d48c588 --- /dev/null +++ b/services/rfq/api/rest/rfq_test.go @@ -0,0 +1,389 @@ +package rest_test + +import ( + "context" + "encoding/json" + "fmt" + "math/big" + + "github.com/shopspring/decimal" + "github.com/synapsecns/sanguine/core/metrics" + "github.com/synapsecns/sanguine/ethergo/signer/signer/localsigner" + "github.com/synapsecns/sanguine/ethergo/signer/wallet" + "github.com/synapsecns/sanguine/services/rfq/api/client" + "github.com/synapsecns/sanguine/services/rfq/api/db" + "github.com/synapsecns/sanguine/services/rfq/api/model" +) + +func runMockRelayer(c *ServerSuite, respCtx context.Context, relayerWallet wallet.Wallet, quoteResp *model.WsRFQResponse, url string) { + // Create a relayer client + relayerSigner := localsigner.NewSigner(relayerWallet.PrivateKey()) + relayerClient, err := client.NewAuthenticatedClient(metrics.Get(), url, relayerSigner) + c.Require().NoError(err) + + // Create channels for active quote requests and responses + reqChan := make(chan *model.ActiveRFQMessage) + req := &model.SubscribeActiveRFQRequest{ + ChainIDs: []int{c.originChainID, c.destChainID}, + } + respChan, err := relayerClient.SubscribeActiveQuotes(c.GetTestContext(), req, reqChan) + c.Require().NoError(err) + + go func() { + for { + select { + case <-respCtx.Done(): + return + case msg := <-respChan: + if msg == nil { + continue + } + if msg.Op == "request_quote" { + var quoteReq model.WsRFQRequest + err := json.Unmarshal(msg.Content, "eReq) + if err != nil { + c.Error(fmt.Errorf("error unmarshaling quote request: %w", err)) + continue + } + quoteResp.RequestID = quoteReq.RequestID + rawRespData, err := json.Marshal(quoteResp) + if err != nil { + c.Error(fmt.Errorf("error marshaling quote response: %w", err)) + continue + } + reqChan <- &model.ActiveRFQMessage{ + Op: "send_quote", + Content: json.RawMessage(rawRespData), + } + } + } + } + }() +} + +func verifyActiveQuoteRequest(c *ServerSuite, userReq *model.PutRFQRequest, activeQuoteRequest *db.ActiveQuoteRequest, status db.ActiveQuoteRequestStatus) { + c.Assert().Equal(uint64(userReq.Data.OriginChainID), activeQuoteRequest.OriginChainID) + c.Assert().Equal(userReq.Data.OriginTokenAddr, activeQuoteRequest.OriginTokenAddr) + c.Assert().Equal(uint64(userReq.Data.DestChainID), activeQuoteRequest.DestChainID) + c.Assert().Equal(userReq.Data.DestTokenAddr, activeQuoteRequest.DestTokenAddr) + c.Assert().Equal(userReq.Data.OriginAmount, activeQuoteRequest.OriginAmount.String()) + c.Assert().Equal(status, activeQuoteRequest.Status) +} + +const ( + originTokenAddr = "0x1111111111111111111111111111111111111111" + destTokenAddr = "0x2222222222222222222222222222222222222222" +) + +func (c *ServerSuite) TestActiveRFQSingleRelayer() { + // Start the API server + c.startQuoterAPIServer() + + url := fmt.Sprintf("http://localhost:%d", c.port) + + // Create a user client + userWallet, err := wallet.FromRandom() + c.Require().NoError(err) + userSigner := localsigner.NewSigner(userWallet.PrivateKey()) + userClient, err := client.NewAuthenticatedClient(metrics.Get(), url, userSigner) + c.Require().NoError(err) + + // Prepare a user quote request + userRequestAmount := big.NewInt(1_000_000) + userQuoteReq := &model.PutRFQRequest{ + Data: model.QuoteData{ + OriginChainID: c.originChainID, + OriginTokenAddr: originTokenAddr, + DestChainID: c.destChainID, + DestTokenAddr: destTokenAddr, + OriginAmount: userRequestAmount.String(), + ExpirationWindow: 10_000, + }, + QuoteTypes: []string{"active"}, + } + + // Prepare the relayer quote response + destAmount := new(big.Int).Sub(userRequestAmount, big.NewInt(1000)).String() + quoteResp := &model.WsRFQResponse{ + DestAmount: destAmount, + } + respCtx, cancel := context.WithCancel(c.GetTestContext()) + defer cancel() + runMockRelayer(c, respCtx, c.relayerWallets[0], quoteResp, url) + + // Submit the user quote request + userQuoteResp, err := userClient.PutRFQRequest(c.GetTestContext(), userQuoteReq) + c.Require().NoError(err) + + // Assert the response + c.Assert().True(userQuoteResp.Success) + c.Assert().Equal("active", userQuoteResp.QuoteType) + c.Assert().Equal(destAmount, userQuoteResp.DestAmount) + + // Verify ActiveQuoteRequest insertion + activeQuoteRequests, err := c.database.GetActiveQuoteRequests(c.GetTestContext()) + c.Require().NoError(err) + verifyActiveQuoteRequest(c, userQuoteReq, activeQuoteRequests[0], db.Closed) +} + +func (c *ServerSuite) TestActiveRFQExpiredRequest() { + // Start the API server + c.startQuoterAPIServer() + + url := fmt.Sprintf("http://localhost:%d", c.port) + + // Create a user client + userWallet, err := wallet.FromRandom() + c.Require().NoError(err) + userSigner := localsigner.NewSigner(userWallet.PrivateKey()) + userClient, err := client.NewAuthenticatedClient(metrics.Get(), url, userSigner) + c.Require().NoError(err) + + // Prepare a user quote request + userRequestAmount := big.NewInt(1_000_000) + userQuoteReq := &model.PutRFQRequest{ + Data: model.QuoteData{ + OriginChainID: c.originChainID, + OriginTokenAddr: originTokenAddr, + DestChainID: c.destChainID, + DestTokenAddr: destTokenAddr, + OriginAmount: userRequestAmount.String(), + ExpirationWindow: 0, + }, + QuoteTypes: []string{"active"}, + } + + // Prepare the relayer quote response + destAmount := new(big.Int).Sub(userRequestAmount, big.NewInt(1000)).String() + quoteResp := &model.WsRFQResponse{ + DestAmount: destAmount, + } + respCtx, cancel := context.WithCancel(c.GetTestContext()) + defer cancel() + runMockRelayer(c, respCtx, c.relayerWallets[0], quoteResp, url) + + // Submit the user quote request + userQuoteResp, err := userClient.PutRFQRequest(c.GetTestContext(), userQuoteReq) + c.Require().NoError(err) + + // Assert the response + c.Assert().False(userQuoteResp.Success) + c.Assert().Equal("no quotes found", userQuoteResp.Reason) + + // Verify ActiveQuoteRequest insertion + activeQuoteRequests, err := c.database.GetActiveQuoteRequests(c.GetTestContext()) + c.Require().NoError(err) + verifyActiveQuoteRequest(c, userQuoteReq, activeQuoteRequests[0], db.Expired) +} + +func (c *ServerSuite) TestActiveRFQMultipleRelayers() { + // Start the API server + c.startQuoterAPIServer() + + url := fmt.Sprintf("http://localhost:%d", c.port) + + // Create a user client + userWallet, err := wallet.FromRandom() + c.Require().NoError(err) + userSigner := localsigner.NewSigner(userWallet.PrivateKey()) + userClient, err := client.NewAuthenticatedClient(metrics.Get(), url, userSigner) + c.Require().NoError(err) + + // Prepare a user quote request + userRequestAmount := big.NewInt(1_000_000) + userQuoteReq := &model.PutRFQRequest{ + Data: model.QuoteData{ + OriginChainID: c.originChainID, + OriginTokenAddr: originTokenAddr, + DestChainID: c.destChainID, + DestTokenAddr: destTokenAddr, + OriginAmount: userRequestAmount.String(), + ExpirationWindow: 10_000, + }, + QuoteTypes: []string{"active"}, + } + + // Prepare the relayer quote responses + destAmount := "999000" + quoteResp := model.WsRFQResponse{ + DestAmount: destAmount, + } + + // Create additional responses with worse prices + destAmount2 := "998000" + quoteResp2 := model.WsRFQResponse{ + DestAmount: destAmount2, + } + destAmount3 := "997000" + quoteResp3 := model.WsRFQResponse{ + DestAmount: destAmount3, + } + respCtx, cancel := context.WithCancel(c.GetTestContext()) + defer cancel() + runMockRelayer(c, respCtx, c.relayerWallets[0], "eResp, url) + runMockRelayer(c, respCtx, c.relayerWallets[1], "eResp2, url) + runMockRelayer(c, respCtx, c.relayerWallets[2], "eResp3, url) + + // Submit the user quote request + userQuoteResp, err := userClient.PutRFQRequest(c.GetTestContext(), userQuoteReq) + c.Require().NoError(err) + + // Assert the response + c.Assert().True(userQuoteResp.Success) + c.Assert().Equal("active", userQuoteResp.QuoteType) + c.Assert().Equal(destAmount, userQuoteResp.DestAmount) + + // Verify ActiveQuoteRequest insertion + activeQuoteRequests, err := c.database.GetActiveQuoteRequests(c.GetTestContext()) + c.Require().NoError(err) + verifyActiveQuoteRequest(c, userQuoteReq, activeQuoteRequests[0], db.Closed) +} + +func (c *ServerSuite) TestActiveRFQFallbackToPassive() { + // Start the API server + c.startQuoterAPIServer() + + url := fmt.Sprintf("http://localhost:%d", c.port) + + // Create a user client + userWallet, err := wallet.FromRandom() + c.Require().NoError(err) + userSigner := localsigner.NewSigner(userWallet.PrivateKey()) + userClient, err := client.NewAuthenticatedClient(metrics.Get(), url, userSigner) + c.Require().NoError(err) + + userRequestAmount := big.NewInt(1_000_000) + + // Upsert passive quotes into the database + passiveQuotes := []db.Quote{ + { + RelayerAddr: c.relayerWallets[0].Address().Hex(), + OriginChainID: uint64(c.originChainID), + OriginTokenAddr: originTokenAddr, + DestChainID: uint64(c.destChainID), + DestTokenAddr: destTokenAddr, + DestAmount: decimal.NewFromBigInt(new(big.Int).Sub(userRequestAmount, big.NewInt(1000)), 0), + MaxOriginAmount: decimal.NewFromBigInt(userRequestAmount, 0), + FixedFee: decimal.NewFromInt(1000), + }, + } + + for _, quote := range passiveQuotes { + err := c.database.UpsertQuote(c.GetTestContext(), "e) + c.Require().NoError(err) + } + + // Prepare user quote request with 0 expiration window + userQuoteReq := &model.PutRFQRequest{ + Data: model.QuoteData{ + OriginChainID: c.originChainID, + OriginTokenAddr: originTokenAddr, + DestChainID: c.destChainID, + DestTokenAddr: destTokenAddr, + OriginAmount: userRequestAmount.String(), + ExpirationWindow: 0, + }, + QuoteTypes: []string{"active", "passive"}, + } + + // Prepare mock relayer response (which should be ignored due to 0 expiration window) + destAmount := new(big.Int).Sub(userRequestAmount, big.NewInt(1000)).String() + quoteResp := &model.WsRFQResponse{ + DestAmount: destAmount, + } + + respCtx, cancel := context.WithCancel(c.GetTestContext()) + defer cancel() + + // Run mock relayer even though we expect it to be ignored + runMockRelayer(c, respCtx, c.relayerWallets[0], quoteResp, url) + + // Submit the user quote request + userQuoteResp, err := userClient.PutRFQRequest(c.GetTestContext(), userQuoteReq) + c.Require().NoError(err) + + // Assert the response + c.Assert().True(userQuoteResp.Success) + c.Assert().Equal("passive", userQuoteResp.QuoteType) + c.Assert().Equal("998000", userQuoteResp.DestAmount) // destAmount is quote destAmount minus fixed fee + c.Assert().Equal(c.relayerWallets[0].Address().Hex(), userQuoteResp.RelayerAddress) +} + +func (c *ServerSuite) TestActiveRFQPassiveBestQuote() { + // Start the API server + c.startQuoterAPIServer() + + url := fmt.Sprintf("http://localhost:%d", c.port) + + // Create a user client + userWallet, err := wallet.FromRandom() + c.Require().NoError(err) + userSigner := localsigner.NewSigner(userWallet.PrivateKey()) + userClient, err := client.NewAuthenticatedClient(metrics.Get(), url, userSigner) + c.Require().NoError(err) + + userRequestAmount := big.NewInt(1_000_000) + + // Upsert passive quotes into the database + passiveQuotes := []db.Quote{ + { + RelayerAddr: c.relayerWallets[0].Address().Hex(), + OriginChainID: uint64(c.originChainID), + OriginTokenAddr: originTokenAddr, + DestChainID: uint64(c.destChainID), + DestTokenAddr: destTokenAddr, + DestAmount: decimal.NewFromBigInt(new(big.Int).Sub(userRequestAmount, big.NewInt(100)), 0), + MaxOriginAmount: decimal.NewFromBigInt(userRequestAmount, 0), + FixedFee: decimal.NewFromInt(1000), + }, + } + + for _, quote := range passiveQuotes { + err := c.database.UpsertQuote(c.GetTestContext(), "e) + c.Require().NoError(err) + } + + // Prepare user quote request with 0 expiration window + userQuoteReq := &model.PutRFQRequest{ + Data: model.QuoteData{ + OriginChainID: c.originChainID, + OriginTokenAddr: originTokenAddr, + DestChainID: c.destChainID, + DestTokenAddr: destTokenAddr, + OriginAmount: userRequestAmount.String(), + ExpirationWindow: 0, + }, + QuoteTypes: []string{"active", "passive"}, + } + + // Prepare mock relayer response (which should be ignored due to 0 expiration window) + destAmount := new(big.Int).Sub(userRequestAmount, big.NewInt(1000)).String() + quoteResp := model.WsRFQResponse{ + DestAmount: destAmount, + } + + respCtx, cancel := context.WithCancel(c.GetTestContext()) + defer cancel() + + // Create additional responses with worse prices + quoteResp2 := quoteResp + destAmount2 := new(big.Int).Sub(userRequestAmount, big.NewInt(2000)) + quoteResp2.DestAmount = destAmount2.String() + quoteResp3 := quoteResp + destAmount3 := new(big.Int).Sub(userRequestAmount, big.NewInt(3000)) + quoteResp3.DestAmount = destAmount3.String() + + runMockRelayer(c, respCtx, c.relayerWallets[0], "eResp, url) + runMockRelayer(c, respCtx, c.relayerWallets[1], "eResp2, url) + runMockRelayer(c, respCtx, c.relayerWallets[2], "eResp3, url) + + // Submit the user quote request + userQuoteResp, err := userClient.PutRFQRequest(c.GetTestContext(), userQuoteReq) + c.Require().NoError(err) + + // Assert the response + c.Assert().True(userQuoteResp.Success) + c.Assert().Equal("passive", userQuoteResp.QuoteType) + c.Assert().Equal("998900", userQuoteResp.DestAmount) // destAmount is quote destAmount minus fixed fee + c.Assert().Equal(c.relayerWallets[0].Address().Hex(), userQuoteResp.RelayerAddress) +} diff --git a/services/rfq/api/rest/server.go b/services/rfq/api/rest/server.go index a3a3b32a6f..5470e0d4f0 100644 --- a/services/rfq/api/rest/server.go +++ b/services/rfq/api/rest/server.go @@ -3,23 +3,28 @@ package rest import ( "context" + "encoding/json" "fmt" "net/http" "sync" "time" + "github.com/google/uuid" "github.com/ipfs/go-log" + "github.com/puzpuzpuz/xsync" swaggerfiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" "github.com/synapsecns/sanguine/core/ginhelper" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/trace" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/gin-gonic/gin" + "github.com/gorilla/websocket" "github.com/jellydator/ttlcache/v3" "github.com/synapsecns/sanguine/core/metrics" baseServer "github.com/synapsecns/sanguine/core/server" @@ -48,6 +53,7 @@ type QuoterAPIServer struct { cfg config.Config db db.APIDB engine *gin.Engine + upgrader websocket.Upgrader omnirpcClient omniClient.RPCClient handler metrics.Handler meter metric.Meter @@ -58,8 +64,11 @@ type QuoterAPIServer struct { relayAckCache *ttlcache.Cache[string, string] // ackMux is a mutex used to ensure that only one transaction id can be acked at a time. ackMux sync.Mutex - // latestQuoteAgeGauge is a gauge that records the age of the latest quote + // latestQuoteAgeGauge is a gauge that records the age of the latest quote. latestQuoteAgeGauge metric.Float64ObservableGauge + // wsClients maintains a mapping of connection ID to a channel for sending quote requests. + wsClients *xsync.MapOf[string, WsClient] + pubSubManager PubSubManager } // NewAPI holds the configuration, database connection, gin engine, RPC client, metrics handler, and fast bridge contracts. @@ -131,6 +140,8 @@ func NewAPI( roleCache: roles, relayAckCache: relayAckCache, ackMux: sync.Mutex{}, + wsClients: xsync.NewMapOf[WsClient](), + pubSubManager: NewPubSubManager(), } // Prometheus metrics setup @@ -157,14 +168,21 @@ const ( AckRoute = "/ack" // ContractsRoute is the API endpoint for returning a list fo contracts. ContractsRoute = "/contracts" - cacheInterval = time.Minute + // RFQStreamRoute is the API endpoint for handling active quote requests via websocket. + RFQStreamRoute = "/rfq_stream" + // RFQRoute is the API endpoint for handling RFQ requests. + RFQRoute = "/rfq" + // ChainsHeader is the header for specifying chains during a websocket handshake. + ChainsHeader = "Chains" + // AuthorizationHeader is the header for specifying the authorization. + AuthorizationHeader = "Authorization" + cacheInterval = time.Minute ) var logger = log.Logger("rfq-api") // Run runs the quoter api server. func (r *QuoterAPIServer) Run(ctx context.Context) error { - // TODO: Use Gin Helper engine := ginhelper.New(logger) h := NewHandler(r.db, r.cfg) @@ -175,7 +193,7 @@ func (r *QuoterAPIServer) Run(ctx context.Context) error { engine.Use(APIVersionMiddleware(versionNumber)) engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler)) - // Apply AuthMiddleware only to the PUT routes + // Authenticated routes quotesPut := engine.Group(QuoteRoute) quotesPut.Use(r.AuthMiddleware()) quotesPut.PUT("", h.ModifyQuote) @@ -185,17 +203,35 @@ func (r *QuoterAPIServer) Run(ctx context.Context) error { ackPut := engine.Group(AckRoute) ackPut.Use(r.AuthMiddleware()) ackPut.PUT("", r.PutRelayAck) - - // GET routes without the AuthMiddleware - // engine.PUT("/quotes", h.ModifyQuote) + openQuoteRequestsGet := engine.Group(RFQRoute) + openQuoteRequestsGet.Use(r.AuthMiddleware()) + openQuoteRequestsGet.GET("", h.GetOpenQuoteRequests) + + // WebSocket route + wsRoute := engine.Group(RFQStreamRoute) + wsRoute.Use(r.AuthMiddleware()) + wsRoute.GET("", func(c *gin.Context) { + r.GetActiveRFQWebsocket(ctx, c) + }) + + // Unauthenticated routes engine.GET(QuoteRoute, h.GetQuotes) - engine.GET(ContractsRoute, h.GetContracts) + engine.PUT(RFQRoute, r.PutRFQRequest) + + // WebSocket upgrader + r.upgrader = websocket.Upgrader{ + CheckOrigin: func(_ *http.Request) bool { + return true // TODO: Implement a more secure check + }, + } r.engine = engine + // Start the main HTTP server connection := baseServer.Server{} fmt.Printf("starting api at http://localhost:%s\n", r.cfg.Port) + err := connection.ListenAndServe(ctx, fmt.Sprintf(":%s", r.cfg.Port), r.engine) if err != nil { return fmt.Errorf("could not start rest api server: %w", err) @@ -205,6 +241,8 @@ func (r *QuoterAPIServer) Run(ctx context.Context) error { } // AuthMiddleware is the Gin authentication middleware that authenticates requests using EIP191. +// +//nolint:gosec func (r *QuoterAPIServer) AuthMiddleware() gin.HandlerFunc { return func(c *gin.Context) { var loggedRequest interface{} @@ -214,7 +252,7 @@ func (r *QuoterAPIServer) AuthMiddleware() gin.HandlerFunc { // Parse the dest chain id from the request switch c.Request.URL.Path { case QuoteRoute: - var req model.PutQuoteRequest + var req model.PutRelayerQuoteRequest err = c.BindJSON(&req) if err == nil { destChainIDs = append(destChainIDs, uint32(req.DestChainID)) @@ -236,6 +274,17 @@ func (r *QuoterAPIServer) AuthMiddleware() gin.HandlerFunc { destChainIDs = append(destChainIDs, uint32(req.DestChainID)) loggedRequest = &req } + case RFQRoute, RFQStreamRoute: + chainsHeader := c.GetHeader(ChainsHeader) + if chainsHeader != "" { + var chainIDs []int + err = json.Unmarshal([]byte(chainsHeader), &chainIDs) + if err == nil { + for _, chainID := range chainIDs { + destChainIDs = append(destChainIDs, uint32(chainID)) + } + } + } default: err = fmt.Errorf("unexpected request path: %s", c.Request.URL.Path) } @@ -325,7 +374,7 @@ func (r *QuoterAPIServer) checkRole(c *gin.Context, destChainID uint32) (address // @Summary Relay ack // @Schemes // @Description cache an ack request to synchronize relayer actions. -// @Param request body model.PutQuoteRequest true "query params" +// @Param request body model.PutRelayerQuoteRequest true "query params" // @Tags ack // @Accept json // @Produce json @@ -374,6 +423,159 @@ func (r *QuoterAPIServer) PutRelayAck(c *gin.Context) { c.JSON(http.StatusOK, resp) } +// GetActiveRFQWebsocket handles the WebSocket connection for active quote requests. +// GET /rfq_stream. +// @Summary Handle WebSocket connection for active quote requests +// @Schemes +// @Description Establish a WebSocket connection to receive active quote requests. +// @Tags quotes +// @Produce json +// @Success 101 {string} string "Switching Protocols" +// @Header 101 {string} X-Api-Version "API Version Number - See docs for more info" +// @Router /rfq_stream [get]. +func (r *QuoterAPIServer) GetActiveRFQWebsocket(ctx context.Context, c *gin.Context) { + ctx, span := r.handler.Tracer().Start(ctx, "GetActiveRFQWebsocket") + defer func() { + metrics.EndSpan(span) + }() + + ws, err := r.upgrader.Upgrade(c.Writer, c.Request, nil) + if err != nil { + logger.Error("Failed to set websocket upgrade", "error", err) + return + } + + // use the relayer address as the ID for the connection + rawRelayerAddr, exists := c.Get("relayerAddr") + if !exists { + c.JSON(http.StatusBadRequest, gin.H{"error": "No relayer address recovered from signature"}) + return + } + relayerAddr, ok := rawRelayerAddr.(string) + if !ok { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid relayer address type"}) + return + } + + span.SetAttributes( + attribute.String("relayer_address", relayerAddr), + ) + + // only one connection per relayer allowed + _, ok = r.wsClients.Load(relayerAddr) + if ok { + c.JSON(http.StatusBadRequest, gin.H{"error": "relayer already connected"}) + return + } + + defer func() { + // cleanup ws registry + r.wsClients.Delete(relayerAddr) + }() + + client := newWsClient(relayerAddr, ws, r.pubSubManager, r.handler) + r.wsClients.Store(relayerAddr, client) + span.AddEvent("registered ws client") + err = client.Run(ctx) + if err != nil { + logger.Error("Error running websocket client", "error", err) + } +} + +const ( + quoteTypeActive = "active" + quoteTypePassive = "passive" +) + +// PutRFQRequest handles a user request for a quote. +// PUT /rfq. +// @Summary Handle user quote request +// @Schemes +// @Description Handle user quote request and return the best quote available. +// @Param request body model.PutRFQRequest true "User quote request" +// @Tags quotes +// @Accept json +// @Produce json +// @Success 200 {object} model.PutRFQResponse +// @Header 200 {string} X-Api-Version "API Version Number - See docs for more info" +// @Router /rfq [put]. +// +//nolint:cyclop +func (r *QuoterAPIServer) PutRFQRequest(c *gin.Context) { + var req model.PutRFQRequest + err := c.BindJSON(&req) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + requestID := uuid.New().String() + ctx, span := r.handler.Tracer().Start(c.Request.Context(), "PutRFQRequest", trace.WithAttributes( + attribute.String("request_id", requestID), + )) + defer func() { + metrics.EndSpan(span) + }() + + err = r.db.InsertActiveQuoteRequest(ctx, &req, requestID) + if err != nil { + logger.Warnf("Error inserting active quote request: %w", err) + } + + var isActiveRFQ bool + for _, quoteType := range req.QuoteTypes { + if quoteType == quoteTypeActive { + isActiveRFQ = true + break + } + } + span.SetAttributes(attribute.Bool("is_active_rfq", isActiveRFQ)) + + // if specified, fetch the active quote. always consider passive quotes + var activeQuote *model.QuoteData + if isActiveRFQ { + activeQuote = r.handleActiveRFQ(ctx, &req, requestID) + if activeQuote != nil && activeQuote.DestAmount != nil { + span.SetAttributes(attribute.String("active_quote_dest_amount", *activeQuote.DestAmount)) + } + } + passiveQuote, err := r.handlePassiveRFQ(ctx, &req) + if err != nil { + logger.Error("Error handling passive RFQ", "error", err) + } + if passiveQuote != nil && passiveQuote.DestAmount != nil { + span.SetAttributes(attribute.String("passive_quote_dest_amount", *passiveQuote.DestAmount)) + } + quote := getBestQuote(activeQuote, passiveQuote) + + // construct the response + var resp model.PutRFQResponse + if quote == nil { + span.AddEvent("no quotes found") + resp = model.PutRFQResponse{ + Success: false, + Reason: "no quotes found", + } + } else { + quoteType := quoteTypeActive + if activeQuote == nil { + quoteType = quoteTypePassive + } + span.SetAttributes( + attribute.String("quote_type", quoteType), + attribute.String("quote_dest_amount", *quote.DestAmount), + ) + resp = model.PutRFQResponse{ + Success: true, + QuoteType: quoteType, + QuoteID: quote.QuoteID, + DestAmount: *quote.DestAmount, + RelayerAddress: *quote.RelayerAddress, + } + } + c.JSON(http.StatusOK, resp) +} + func (r *QuoterAPIServer) recordLatestQuoteAge(ctx context.Context, observer metric.Observer) (err error) { if r.handler == nil || r.latestQuoteAgeGauge == nil { return nil diff --git a/services/rfq/api/rest/server_test.go b/services/rfq/api/rest/server_test.go index 50245f6ac4..c82db8d6fc 100644 --- a/services/rfq/api/rest/server_test.go +++ b/services/rfq/api/rest/server_test.go @@ -125,9 +125,6 @@ func (c *ServerSuite) TestEIP191_UnsuccessfulSignature() { err = resp.Body.Close() c.Require().NoError(err) }() - // Log the response body for debugging. - body, _ := io.ReadAll(resp.Body) - fmt.Println(string(body)) // Assert that the response status code is HTTP 400 Bad Request. c.Equal(http.StatusBadRequest, resp.StatusCode) @@ -152,11 +149,6 @@ func (c *ServerSuite) TestEIP191_SuccessfulPutSubmission() { // Check for X-Api-Version on the response c.Equal(resp.Header.Get("X-Api-Version"), rest.APIversions.Versions[0].Version) - // Log the response body for debugging. - body, err := io.ReadAll(resp.Body) - c.Require().NoError(err) - fmt.Println(string(body)) - // Assert that the response status code is HTTP 200 OK. c.Assert().Equal(http.StatusOK, resp.StatusCode) } @@ -209,6 +201,87 @@ func (c *ServerSuite) TestPutAndGetQuote() { c.Assert().True(found, "Newly added quote not found") } +func (c *ServerSuite) TestGetOpenQuoteRequests() { + // Start the API server + c.startQuoterAPIServer() + + // Insert some test quote requests + testRequests := []*model.PutRFQRequest{ + { + Data: model.QuoteData{ + OriginChainID: 1, + DestChainID: 42161, + OriginTokenAddr: "0xOriginTokenAddr", + DestTokenAddr: "0xDestTokenAddr", + OriginAmount: "100.0", + ExpirationWindow: 100, + }, + }, + { + Data: model.QuoteData{ + OriginChainID: 1, + DestChainID: 42161, + OriginTokenAddr: "0xOriginTokenAddr", + DestTokenAddr: "0xDestTokenAddr", + OriginAmount: "100.0", + ExpirationWindow: 100, + }, + }, + { + Data: model.QuoteData{ + OriginChainID: 1, + DestChainID: 42161, + OriginTokenAddr: "0xOriginTokenAddr", + DestTokenAddr: "0xDestTokenAddr", + OriginAmount: "100.0", + ExpirationWindow: 100, + }, + }, + } + + statuses := []db.ActiveQuoteRequestStatus{db.Received, db.Pending, db.Expired} + for i, req := range testRequests { + err := c.database.InsertActiveQuoteRequest(c.GetTestContext(), req, strconv.Itoa(i)) + c.Require().NoError(err) + err = c.database.UpdateActiveQuoteRequestStatus(c.GetTestContext(), strconv.Itoa(i), nil, statuses[i]) + c.Require().NoError(err) + } + + // Prepare the authorization header + header, err := c.prepareAuthHeader(c.testWallet) + c.Require().NoError(err) + + // Send GET request to fetch open quote requests + client := &http.Client{} + req, err := http.NewRequestWithContext(c.GetTestContext(), http.MethodGet, fmt.Sprintf("http://localhost:%d%s", c.port, rest.RFQRoute), nil) + c.Require().NoError(err) + req.Header.Add("Authorization", header) + chainIDsJSON, err := json.Marshal([]uint64{1, 42161}) + c.Require().NoError(err) + req.Header.Add("Chains", string(chainIDsJSON)) + + resp, err := client.Do(req) + c.Require().NoError(err) + defer func() { + err = resp.Body.Close() + c.Require().NoError(err) + }() + + // Check the response status code + c.Assert().Equal(http.StatusOK, resp.StatusCode) + + // Check for X-Api-Version on the response + c.Equal(resp.Header.Get("X-Api-Version"), rest.APIversions.Versions[0].Version) + + // Parse the response body + var openRequests []*model.GetOpenQuoteRequestsResponse + err = json.NewDecoder(resp.Body).Decode(&openRequests) + c.Require().NoError(err) + + // Verify the number of open requests (should be 2: Received and Pending) + c.Assert().Len(openRequests, 2) +} + func (c *ServerSuite) TestPutAndGetQuoteByRelayer() { c.startQuoterAPIServer() @@ -285,9 +358,6 @@ func (c *ServerSuite) TestMultiplePutRequestsWithIncorrectAuth() { // Check for X-Api-Version on the response c.Equal(resp.Header.Get("X-Api-Version"), rest.APIversions.Versions[0].Version) - // Log the response body for debugging - fmt.Printf("Request %d response: Status: %d, Body: %s\n", i+1, resp.StatusCode, string(body)) - switch resp.StatusCode { case http.StatusBadRequest, http.StatusUnauthorized, http.StatusForbidden: // These are acceptable error status codes for failed authentication @@ -401,9 +471,7 @@ func (c *ServerSuite) prepareAuthHeader(wallet wallet.Wallet) (string, error) { func (c *ServerSuite) sendPutQuoteRequest(header string) (*http.Response, error) { // Prepare the PUT request with JSON data. client := &http.Client{} - putData := model.PutQuoteRequest{ - OriginChainID: 1, - OriginTokenAddr: "0xOriginTokenAddr", + putData := model.PutRelayerQuoteRequest{ DestChainID: 42161, DestTokenAddr: "0xDestTokenAddr", DestAmount: "100.0", diff --git a/services/rfq/api/rest/suite_test.go b/services/rfq/api/rest/suite_test.go index 112e957ae1..755b4882ca 100644 --- a/services/rfq/api/rest/suite_test.go +++ b/services/rfq/api/rest/suite_test.go @@ -40,29 +40,37 @@ type ServerSuite struct { database db.APIDB cfg config.Config testWallet wallet.Wallet + relayerWallets []wallet.Wallet handler metrics.Handler QuoterAPIServer *rest.QuoterAPIServer port uint16 + originChainID int + destChainID int } // NewServerSuite creates a end-to-end test suite. func NewServerSuite(tb testing.TB) *ServerSuite { tb.Helper() return &ServerSuite{ - TestSuite: testsuite.NewTestSuite(tb), + TestSuite: testsuite.NewTestSuite(tb), + relayerWallets: []wallet.Wallet{}, } } +//nolint:gosec func (c *ServerSuite) SetupTest() { c.TestSuite.SetupTest() + c.setDB() testOmnirpc := omnirpcHelper.NewOmnirpcServer(c.GetTestContext(), c.T(), c.omniRPCTestBackends...) omniRPCClient := omniClient.NewOmnirpcClient(testOmnirpc, c.handler, omniClient.WithCaptureReqRes()) c.omniRPCClient = omniRPCClient - arbFastBridgeAddress, ok := c.fastBridgeAddressMap.Load(42161) + c.originChainID = 1 + c.destChainID = 42161 + arbFastBridgeAddress, ok := c.fastBridgeAddressMap.Load(uint64(c.destChainID)) c.True(ok) - ethFastBridgeAddress, ok := c.fastBridgeAddressMap.Load(1) + ethFastBridgeAddress, ok := c.fastBridgeAddressMap.Load(uint64(c.originChainID)) c.True(ok) port, err := freeport.GetFreePort() c.port = uint16(port) @@ -132,8 +140,16 @@ func (c *ServerSuite) SetupSuite() { testWallet, err := wallet.FromRandom() c.Require().NoError(err) c.testWallet = testWallet + c.relayerWallets = []wallet.Wallet{c.testWallet} + for range [2]int{} { + relayerWallet, err := wallet.FromRandom() + c.Require().NoError(err) + c.relayerWallets = append(c.relayerWallets, relayerWallet) + } for _, backend := range c.testBackends { - backend.FundAccount(c.GetSuiteContext(), c.testWallet.Address(), *big.NewInt(params.Ether)) + for _, relayerWallet := range c.relayerWallets { + backend.FundAccount(c.GetSuiteContext(), relayerWallet.Address(), *big.NewInt(params.Ether)) + } } c.fastBridgeAddressMap = xsync.NewIntegerMapOf[uint64, common.Address]() @@ -163,9 +179,12 @@ func (c *ServerSuite) SetupSuite() { relayerRole, err := fastBridgeInstance.RELAYERROLE(&bind.CallOpts{Context: c.GetTestContext()}) c.NoError(err) - tx, err = fastBridgeInstance.GrantRole(auth, relayerRole, c.testWallet.Address()) - c.Require().NoError(err) - backend.WaitForConfirmation(c.GetSuiteContext(), tx) + // Grant relayer role to all relayer wallets + for _, relayerWallet := range c.relayerWallets { + tx, err = fastBridgeInstance.GrantRole(auth, relayerRole, relayerWallet.Address()) + c.Require().NoError(err) + backend.WaitForConfirmation(c.GetSuiteContext(), tx) + } return nil }) @@ -175,7 +194,10 @@ func (c *ServerSuite) SetupSuite() { if err := g.Wait(); err != nil { c.T().Fatal(err) } + // setup config +} +func (c *ServerSuite) setDB() { dbType, err := dbcommon.DBTypeFromString("sqlite") c.Require().NoError(err) metricsHandler := metrics.NewNullHandler() @@ -183,7 +205,6 @@ func (c *ServerSuite) SetupSuite() { // TODO use temp file / in memory sqlite3 to not create in directory files testDB, _ := sql.Connect(c.GetSuiteContext(), dbType, filet.TmpDir(c.T(), ""), metricsHandler) c.database = testDB - // setup config } // TestConfigSuite runs the integration test suite. diff --git a/services/rfq/api/rest/ws.go b/services/rfq/api/rest/ws.go new file mode 100644 index 0000000000..99ba0cfa19 --- /dev/null +++ b/services/rfq/api/rest/ws.go @@ -0,0 +1,340 @@ +package rest + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/gorilla/websocket" + "github.com/puzpuzpuz/xsync" + "github.com/synapsecns/sanguine/core/metrics" + "github.com/synapsecns/sanguine/services/rfq/api/model" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + "golang.org/x/sync/errgroup" +) + +// WsClient is a client for the WebSocket API. +type WsClient interface { + Run(ctx context.Context) error + SendQuoteRequest(ctx context.Context, quoteRequest *model.WsRFQRequest) error + ReceiveQuoteResponse(ctx context.Context, requestID string) (*model.WsRFQResponse, error) +} + +type wsClient struct { + handler metrics.Handler + relayerAddr string + conn *websocket.Conn + pubsub PubSubManager + requestChan chan *model.WsRFQRequest + responseChans *xsync.MapOf[string, chan *model.WsRFQResponse] + doneChan chan struct{} + pingTicker *time.Ticker + lastPing time.Time +} + +func newWsClient(relayerAddr string, conn *websocket.Conn, pubsub PubSubManager, handler metrics.Handler) *wsClient { + return &wsClient{ + handler: handler, + relayerAddr: relayerAddr, + conn: conn, + pubsub: pubsub, + requestChan: make(chan *model.WsRFQRequest), + responseChans: xsync.NewMapOf[chan *model.WsRFQResponse](), + doneChan: make(chan struct{}), + pingTicker: time.NewTicker(pingPeriod), + } +} + +func (c *wsClient) SendQuoteRequest(ctx context.Context, quoteRequest *model.WsRFQRequest) error { + select { + case c.requestChan <- quoteRequest: + // successfully sent, register a response channel + c.responseChans.Store(quoteRequest.RequestID, make(chan *model.WsRFQResponse)) + case <-c.doneChan: + return fmt.Errorf("websocket client is closed") + case <-ctx.Done(): + return nil + } + return nil +} + +func (c *wsClient) ReceiveQuoteResponse(ctx context.Context, requestID string) (resp *model.WsRFQResponse, err error) { + responseChan, ok := c.responseChans.Load(requestID) + if !ok { + return nil, fmt.Errorf("no response channel for request %s", requestID) + } + defer c.responseChans.Delete(requestID) + + for { + select { + case resp = <-responseChan: + // successfully received + return resp, nil + case <-c.doneChan: + return nil, fmt.Errorf("websocket client is closed") + case <-ctx.Done(): + return nil, fmt.Errorf("expiration reached without response") + } + } +} + +const ( + // PongOp is the operation for a pong message. + PongOp = "pong" + // PingOp is the operation for a ping message. + PingOp = "ping" + // SubscribeOp is the operation for a subscribe message. + SubscribeOp = "subscribe" + // UnsubscribeOp is the operation for an unsubscribe message. + UnsubscribeOp = "unsubscribe" + // RequestQuoteOp is the operation for a request quote message. + RequestQuoteOp = "request_quote" + // SendQuoteOp is the operation for a send quote message. + SendQuoteOp = "send_quote" + // pingPeriod is the period for a ping message. + pingPeriod = 1 * time.Minute +) + +// Run runs the WebSocket client. +func (c *wsClient) Run(ctx context.Context) (err error) { + messageChan := make(chan []byte) + + g, gctx := errgroup.WithContext(ctx) + g.Go(func() error { + err := pollWsMessages(gctx, c.conn, messageChan) + if err != nil { + return fmt.Errorf("error polling websocket messages: %w", err) + } + return nil + }) + g.Go(func() error { + err := c.processWs(gctx, messageChan) + if err != nil { + return fmt.Errorf("error processing websocket messages: %w", err) + } + return nil + }) + + err = g.Wait() + if err != nil { + return fmt.Errorf("error running websocket client: %w", err) + } + + return nil +} + +func pollWsMessages(ctx context.Context, conn *websocket.Conn, messageChan chan []byte) (err error) { + defer close(messageChan) + for { + _, msg, err := conn.ReadMessage() + if err != nil { + return fmt.Errorf("error reading websocket message: %w", err) + } + select { + case <-ctx.Done(): + return nil + case messageChan <- msg: + } + } +} + +func (c *wsClient) processWs(ctx context.Context, messageChan chan []byte) (err error) { + defer c.pingTicker.Stop() + + for { + select { + case <-ctx.Done(): + err = c.conn.Close() + if err != nil { + return fmt.Errorf("error closing websocket connection: %w", err) + } + close(c.doneChan) + return fmt.Errorf("websocket client is closed") + case req := <-c.requestChan: + err = c.sendRelayerRequest(ctx, req) + if err != nil { + logger.Error("Error sending quote request: %s", err) + } + case msg := <-messageChan: + err = c.handleRelayerMessage(ctx, msg) + if err != nil { + logger.Error("Error handling relayer message: %s", err) + return fmt.Errorf("error handling relayer message: %w", err) + } + case <-c.pingTicker.C: + // ping timed out, close the connection + _, span := c.handler.Tracer().Start(ctx, "pingTimeout") + defer metrics.EndSpanWithErr(span, err) + } + } +} + +func (c *wsClient) sendRelayerRequest(ctx context.Context, req *model.WsRFQRequest) (err error) { + _, span := c.handler.Tracer().Start(ctx, "sendRelayerRequest", trace.WithAttributes( + attribute.String("relayer_address", c.relayerAddr), + attribute.String("request_id", req.RequestID), + )) + defer func() { + metrics.EndSpan(span) + }() + + rawData, err := json.Marshal(req) + if err != nil { + return fmt.Errorf("error marshaling quote request: %w", err) + } + msg := model.ActiveRFQMessage{ + Op: RequestQuoteOp, + Content: json.RawMessage(rawData), + } + err = c.conn.WriteJSON(msg) + if err != nil { + return fmt.Errorf("error sending quote request: %w", err) + } + + return nil +} + +// handleRelayerMessage handles messages from the relayer. +// An error returned will result in the websocket connection being closed. +func (c *wsClient) handleRelayerMessage(ctx context.Context, msg []byte) (err error) { + _, span := c.handler.Tracer().Start(ctx, "handleRelayerMessage", trace.WithAttributes( + attribute.String("relayer_address", c.relayerAddr), + attribute.String("message", string(msg)), + )) + defer func() { + metrics.EndSpanWithErr(span, err) + }() + + var rfqMsg model.ActiveRFQMessage + err = json.Unmarshal(msg, &rfqMsg) + if err != nil { + return fmt.Errorf("error unmarshaling websocket message: %w", err) + } + + switch rfqMsg.Op { + case PingOp: + c.lastPing = time.Now() + resp := c.handlePing(ctx) + err = c.conn.WriteJSON(resp) + if err != nil { + return fmt.Errorf("error sending ping response: %w", err) + } + case SubscribeOp: + resp := c.handleSubscribe(ctx, rfqMsg.Content) + err = c.conn.WriteJSON(resp) + if err != nil { + return fmt.Errorf("error sending subscribe response: %w", err) + } + case UnsubscribeOp: + resp := c.handleUnsubscribe(ctx, rfqMsg.Content) + err = c.conn.WriteJSON(resp) + if err != nil { + return fmt.Errorf("error sending unsubscribe response: %w", err) + } + case SendQuoteOp: + err = c.handleSendQuote(ctx, rfqMsg.Content) + logger.Errorf("error handling send quote: %v", err) + default: + logger.Errorf("received unexpected operation from relayer: %s", rfqMsg.Op) + return nil + } + + return nil +} + +func (c *wsClient) handlePing(ctx context.Context) (resp model.ActiveRFQMessage) { + _, span := c.handler.Tracer().Start(ctx, "handlePing", trace.WithAttributes( + attribute.String("relayer_address", c.relayerAddr), + )) + defer func() { + metrics.EndSpan(span) + }() + + return getSuccessResponse(PongOp) +} + +func (c *wsClient) handleSubscribe(ctx context.Context, content json.RawMessage) (resp model.ActiveRFQMessage) { + _, span := c.handler.Tracer().Start(ctx, "handleSubscribe", trace.WithAttributes( + attribute.String("relayer_address", c.relayerAddr), + )) + defer func() { + metrics.EndSpan(span) + }() + + var sub model.SubscriptionParams + err := json.Unmarshal(content, &sub) + if err != nil { + return getErrorResponse(SubscribeOp, fmt.Errorf("could not unmarshal subscription params: %w", err)) + } + span.SetAttributes(attribute.IntSlice("chain_ids", sub.Chains)) + err = c.pubsub.AddSubscription(c.relayerAddr, sub) + if err != nil { + return getErrorResponse(SubscribeOp, fmt.Errorf("error adding subscription: %w", err)) + } + return getSuccessResponse(SubscribeOp) +} + +func (c *wsClient) handleUnsubscribe(ctx context.Context, content json.RawMessage) (resp model.ActiveRFQMessage) { + _, span := c.handler.Tracer().Start(ctx, "handleUnsubscribe", trace.WithAttributes( + attribute.String("relayer_address", c.relayerAddr), + )) + defer func() { + metrics.EndSpan(span) + }() + + var sub model.SubscriptionParams + err := json.Unmarshal(content, &sub) + if err != nil { + return getErrorResponse(UnsubscribeOp, fmt.Errorf("could not unmarshal subscription params: %w", err)) + } + span.SetAttributes(attribute.IntSlice("chain_ids", sub.Chains)) + err = c.pubsub.RemoveSubscription(c.relayerAddr, sub) + if err != nil { + return getErrorResponse(UnsubscribeOp, fmt.Errorf("error removing subscription: %w", err)) + } + return getSuccessResponse(UnsubscribeOp) +} + +func (c *wsClient) handleSendQuote(ctx context.Context, content json.RawMessage) (err error) { + _, span := c.handler.Tracer().Start(ctx, "handleSendQuote", trace.WithAttributes( + attribute.String("relayer_address", c.relayerAddr), + )) + defer func() { + metrics.EndSpan(span) + }() + + // forward the response to the server + var resp model.WsRFQResponse + err = json.Unmarshal(content, &resp) + if err != nil { + return fmt.Errorf("error unmarshaling websocket message: %w", err) + } + span.SetAttributes( + attribute.String("request_id", resp.RequestID), + attribute.String("dest_amount", resp.DestAmount), + ) + responseChan, ok := c.responseChans.Load(resp.RequestID) + if !ok { + return fmt.Errorf("no response channel for request %s", resp.RequestID) + } + responseChan <- &resp + + return nil +} + +func getSuccessResponse(op string) model.ActiveRFQMessage { + return model.ActiveRFQMessage{ + Op: op, + Success: true, + } +} + +func getErrorResponse(op string, err error) model.ActiveRFQMessage { + return model.ActiveRFQMessage{ + Op: op, + Success: false, + Content: json.RawMessage(fmt.Sprintf("{\"reason\": \"%s\"}", err.Error())), + } +} diff --git a/services/rfq/e2e/setup_test.go b/services/rfq/e2e/setup_test.go index af4d93a0a5..d95366642d 100644 --- a/services/rfq/e2e/setup_test.go +++ b/services/rfq/e2e/setup_test.go @@ -314,7 +314,7 @@ func (i *IntegrationSuite) getRelayerConfig() relconfig.Config { }, // generated ex-post facto QuotableTokens: map[string][]string{}, - RfqAPIURL: i.apiServer, + RFQAPIURL: i.apiServer, Signer: signerConfig.SignerConfig{ Type: signerConfig.FileType.String(), File: filet.TmpFile(i.T(), "", i.relayerWallet.PrivateKeyHex()).Name(), diff --git a/services/rfq/go.mod b/services/rfq/go.mod index b463b6c502..3c24fbf857 100644 --- a/services/rfq/go.mod +++ b/services/rfq/go.mod @@ -16,11 +16,13 @@ require ( github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a github.com/go-resty/resty/v2 v2.13.1 github.com/google/uuid v1.6.0 + github.com/gorilla/websocket v1.5.3 github.com/ipfs/go-log v1.0.5 github.com/jellydator/ttlcache/v3 v3.1.1 github.com/jftuga/ellipsis v1.0.0 github.com/lmittmann/w3 v0.10.0 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 + github.com/puzpuzpuz/xsync v1.5.2 github.com/puzpuzpuz/xsync/v2 v2.5.1 github.com/shopspring/decimal v1.4.0 github.com/stretchr/testify v1.9.0 @@ -176,7 +178,6 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.4 // indirect - github.com/gorilla/websocket v1.5.3 // indirect github.com/grafana/otel-profiling-go v0.5.1 // indirect github.com/grafana/pyroscope-go v1.1.1 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.7 // indirect @@ -251,7 +252,6 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.54.0 // indirect github.com/prometheus/procfs v0.15.0 // indirect - github.com/puzpuzpuz/xsync v1.5.2 // indirect github.com/rbretecher/go-postman-collection v0.9.0 // indirect github.com/richardwilkes/toolbox v1.74.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect diff --git a/services/rfq/relayer/limiter/limiter_test.go b/services/rfq/relayer/limiter/limiter_test.go index 397c3da218..03e224ed99 100644 --- a/services/rfq/relayer/limiter/limiter_test.go +++ b/services/rfq/relayer/limiter/limiter_test.go @@ -82,6 +82,7 @@ func (l *LimiterSuite) TestUnderLimitNotEnoughConfirmations() { } func (l *LimiterSuite) TestOverLimitNotEnoughConfirmations() { + l.T().Skip() mockQuoter := buildMockQuoter(69420) mockListener := buildMockListener(4) diff --git a/services/rfq/relayer/quoter/export_test.go b/services/rfq/relayer/quoter/export_test.go index 81d719ae03..66817a0ce5 100644 --- a/services/rfq/relayer/quoter/export_test.go +++ b/services/rfq/relayer/quoter/export_test.go @@ -9,7 +9,7 @@ import ( "github.com/synapsecns/sanguine/services/rfq/relayer/relconfig" ) -func (m *Manager) GenerateQuotes(ctx context.Context, chainID int, address common.Address, balance *big.Int, inv map[int]map[common.Address]*big.Int) ([]model.PutQuoteRequest, error) { +func (m *Manager) GenerateQuotes(ctx context.Context, chainID int, address common.Address, balance *big.Int, inv map[int]map[common.Address]*big.Int) ([]model.PutRelayerQuoteRequest, error) { // nolint: errcheck return m.generateQuotes(ctx, chainID, address, balance, inv) } diff --git a/services/rfq/relayer/quoter/mocks/quoter.go b/services/rfq/relayer/quoter/mocks/quoter.go index 0832d49109..c794de8e03 100644 --- a/services/rfq/relayer/quoter/mocks/quoter.go +++ b/services/rfq/relayer/quoter/mocks/quoter.go @@ -92,6 +92,20 @@ func (_m *Quoter) SubmitAllQuotes(ctx context.Context) error { return r0 } +// SubscribeActiveRFQ provides a mock function with given fields: ctx +func (_m *Quoter) SubscribeActiveRFQ(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + type mockConstructorTestingTNewQuoter interface { mock.TestingT Cleanup(func()) diff --git a/services/rfq/relayer/quoter/quoter.go b/services/rfq/relayer/quoter/quoter.go index b1bfc0db9c..3b5ecc8352 100644 --- a/services/rfq/relayer/quoter/quoter.go +++ b/services/rfq/relayer/quoter/quoter.go @@ -3,6 +3,7 @@ package quoter import ( "context" + "encoding/json" "errors" "fmt" "math/big" @@ -31,6 +32,7 @@ import ( "github.com/synapsecns/sanguine/ethergo/signer/signer" rfqAPIClient "github.com/synapsecns/sanguine/services/rfq/api/client" "github.com/synapsecns/sanguine/services/rfq/api/model" + "github.com/synapsecns/sanguine/services/rfq/api/rest" "github.com/synapsecns/sanguine/services/rfq/relayer/inventory" ) @@ -42,6 +44,8 @@ var logger = log.Logger("quoter") type Quoter interface { // SubmitAllQuotes submits all quotes to the RFQ API. SubmitAllQuotes(ctx context.Context) (err error) + // SubscribeActiveRFQ subscribes to the RFQ websocket API. + SubscribeActiveRFQ(ctx context.Context) (err error) // ShouldProcess determines if a quote should be processed. // We do this by either saving all quotes in-memory, and refreshing via GetSelfQuotes() through the API // The first comparison is does bridge transaction OriginChainID+TokenAddr match with a quote + DestChainID+DestTokenAddr, then we look to see if we have enough amount to relay it + if the price fits our bounds (based on that the Relayer is relaying the destination token for the origin) @@ -81,7 +85,7 @@ type Manager struct { // quoteAmountGauge stores a histogram of quote amounts. quoteAmountGauge metric.Float64ObservableGauge // currentQuotes is used for recording quote metrics. - currentQuotes []model.PutQuoteRequest + currentQuotes []model.PutRelayerQuoteRequest } // NewQuoterManager creates a new QuoterManager. @@ -123,7 +127,7 @@ func NewQuoterManager(config relconfig.Config, metricsHandler metrics.Handler, i feePricer: feePricer, screener: ss, meter: metricsHandler.Meter(meterName), - currentQuotes: []model.PutQuoteRequest{}, + currentQuotes: []model.PutRelayerQuoteRequest{}, } m.quoteAmountGauge, err = m.meter.Float64ObservableGauge("quote_amount") @@ -251,6 +255,111 @@ func (m *Manager) SubmitAllQuotes(ctx context.Context) (err error) { return m.prepareAndSubmitQuotes(ctx, inv) } +// SubscribeActiveRFQ subscribes to the RFQ websocket API. +// This function is blocking and will run until the context is canceled. +func (m *Manager) SubscribeActiveRFQ(ctx context.Context) (err error) { + ctx, span := m.metricsHandler.Tracer().Start(ctx, "SubscribeActiveRFQ") + defer func() { + metrics.EndSpanWithErr(span, err) + }() + + chainIDs := []int{} + for chainID := range m.config.Chains { + chainIDs = append(chainIDs, chainID) + } + req := model.SubscribeActiveRFQRequest{ + ChainIDs: chainIDs, + } + span.SetAttributes(attribute.IntSlice("chain_ids", chainIDs)) + + reqChan := make(chan *model.ActiveRFQMessage) + respChan, err := m.rfqClient.SubscribeActiveQuotes(ctx, &req, reqChan) + if err != nil { + return fmt.Errorf("error subscribing to active quotes: %w", err) + } + span.AddEvent("subscribed to active quotes") + for { + select { + case <-ctx.Done(): + return nil + case msg, ok := <-respChan: + if !ok { + return errors.New("ws channel closed") + } + if msg == nil { + continue + } + resp, err := m.generateActiveRFQ(ctx, msg) + if err != nil { + return fmt.Errorf("error generating active RFQ message: %w", err) + } + reqChan <- resp + } + } +} + +// getActiveRFQ handles an active RFQ message. +// +//nolint:nilnil +func (m *Manager) generateActiveRFQ(ctx context.Context, msg *model.ActiveRFQMessage) (resp *model.ActiveRFQMessage, err error) { + ctx, span := m.metricsHandler.Tracer().Start(ctx, "generateActiveRFQ", trace.WithAttributes( + attribute.String("op", msg.Op), + attribute.String("content", string(msg.Content)), + )) + defer func() { + metrics.EndSpanWithErr(span, err) + }() + + if msg.Op != rest.RequestQuoteOp { + span.AddEvent("not a request quote op") + return nil, nil + } + + inv, err := m.inventoryManager.GetCommittableBalances(ctx, inventory.SkipDBCache()) + if err != nil { + return nil, fmt.Errorf("error getting committable balances: %w", err) + } + + var rfqRequest model.WsRFQRequest + err = json.Unmarshal(msg.Content, &rfqRequest) + if err != nil { + return nil, fmt.Errorf("error unmarshalling quote data: %w", err) + } + span.SetAttributes(attribute.String("request_id", rfqRequest.RequestID)) + + quoteInput := QuoteInput{ + OriginChainID: rfqRequest.Data.OriginChainID, + DestChainID: rfqRequest.Data.DestChainID, + OriginTokenAddr: common.HexToAddress(rfqRequest.Data.OriginTokenAddr), + DestTokenAddr: common.HexToAddress(rfqRequest.Data.DestTokenAddr), + OriginBalance: inv[rfqRequest.Data.OriginChainID][common.HexToAddress(rfqRequest.Data.OriginTokenAddr)], + DestBalance: inv[rfqRequest.Data.DestChainID][common.HexToAddress(rfqRequest.Data.DestTokenAddr)], + } + + rawQuote, err := m.generateQuote(ctx, quoteInput) + if err != nil { + return nil, fmt.Errorf("error generating quote: %w", err) + } + span.SetAttributes(attribute.String("dest_amount", rawQuote.DestAmount)) + + rfqResp := model.WsRFQResponse{ + RequestID: rfqRequest.RequestID, + DestAmount: rawQuote.DestAmount, + } + span.SetAttributes(attribute.String("dest_amount", rawQuote.DestAmount)) + respBytes, err := json.Marshal(rfqResp) + if err != nil { + return nil, fmt.Errorf("error serializing response: %w", err) + } + resp = &model.ActiveRFQMessage{ + Op: rest.SendQuoteOp, + Content: respBytes, + } + span.AddEvent("generated response") + + return resp, nil +} + // GetPrice gets the price of a token. func (m *Manager) GetPrice(parentCtx context.Context, tokenName string) (_ float64, err error) { ctx, span := m.metricsHandler.Tracer().Start(parentCtx, "GetPrice") @@ -274,7 +383,7 @@ func (m *Manager) prepareAndSubmitQuotes(ctx context.Context, inv map[int]map[co metrics.EndSpanWithErr(span, err) }() - var allQuotes []model.PutQuoteRequest + var allQuotes []model.PutRelayerQuoteRequest // First, generate all quotes g, gctx := errgroup.WithContext(ctx) @@ -343,7 +452,7 @@ const meterName = "github.com/synapsecns/sanguine/services/rfq/relayer/quoter" // Essentially, if we know a destination chain token balance, then we just need to find which tokens are bridgeable to it. // We can do this by looking at the quotableTokens map, and finding the key that matches the destination chain token. // Generates quotes for a given chain ID, address, and balance. -func (m *Manager) generateQuotes(parentCtx context.Context, chainID int, address common.Address, balance *big.Int, inv map[int]map[common.Address]*big.Int) (quotes []model.PutQuoteRequest, err error) { +func (m *Manager) generateQuotes(parentCtx context.Context, chainID int, address common.Address, balance *big.Int, inv map[int]map[common.Address]*big.Int) (quotes []model.PutRelayerQuoteRequest, err error) { ctx, span := m.metricsHandler.Tracer().Start(parentCtx, "generateQuotes", trace.WithAttributes( attribute.Int(metrics.Origin, chainID), attribute.String("address", address.String()), @@ -362,7 +471,7 @@ func (m *Manager) generateQuotes(parentCtx context.Context, chainID int, address // generate quotes in parallel g, gctx := errgroup.WithContext(ctx) quoteMtx := &sync.Mutex{} - quotes = []model.PutQuoteRequest{} + quotes = []model.PutRelayerQuoteRequest{} for k, itemTokenIDs := range m.quotableTokens { for _, tokenID := range itemTokenIDs { //nolint:nestif @@ -433,7 +542,7 @@ type QuoteInput struct { DestRFQAddr string } -func (m *Manager) generateQuote(ctx context.Context, input QuoteInput) (quote *model.PutQuoteRequest, err error) { +func (m *Manager) generateQuote(ctx context.Context, input QuoteInput) (quote *model.PutRelayerQuoteRequest, err error) { // Calculate the quote amount for this route originAmount, err := m.getOriginAmount(ctx, input) // don't quote if gas exceeds quote @@ -467,7 +576,7 @@ func (m *Manager) generateQuote(ctx context.Context, input QuoteInput) (quote *m logger.Error("Error getting dest amount", "error", err) return nil, fmt.Errorf("error getting dest amount: %w", err) } - quote = &model.PutQuoteRequest{ + quote = &model.PutRelayerQuoteRequest{ OriginChainID: input.OriginChainID, OriginTokenAddr: input.OriginTokenAddr.Hex(), DestChainID: input.DestChainID, @@ -700,7 +809,7 @@ func (m *Manager) applyOffset(parentCtx context.Context, offsetBps float64, targ } // Submits a single quote. -func (m *Manager) submitQuote(ctx context.Context, quote model.PutQuoteRequest) error { +func (m *Manager) submitQuote(ctx context.Context, quote model.PutRelayerQuoteRequest) error { quoteCtx, quoteCancel := context.WithTimeout(ctx, m.config.GetQuoteSubmissionTimeout()) defer quoteCancel() @@ -712,7 +821,7 @@ func (m *Manager) submitQuote(ctx context.Context, quote model.PutQuoteRequest) } // Submits multiple quotes. -func (m *Manager) submitBulkQuotes(ctx context.Context, quotes []model.PutQuoteRequest) error { +func (m *Manager) submitBulkQuotes(ctx context.Context, quotes []model.PutRelayerQuoteRequest) error { quoteCtx, quoteCancel := context.WithTimeout(ctx, m.config.GetQuoteSubmissionTimeout()) defer quoteCancel() diff --git a/services/rfq/relayer/quoter/quoter_test.go b/services/rfq/relayer/quoter/quoter_test.go index 328ba60eaa..1d6a52c7de 100644 --- a/services/rfq/relayer/quoter/quoter_test.go +++ b/services/rfq/relayer/quoter/quoter_test.go @@ -27,7 +27,7 @@ func (s *QuoterSuite) TestGenerateQuotes() { s.Require().NoError(err) // Verify the quotes are generated as expected. - expectedQuotes := []model.PutQuoteRequest{ + expectedQuotes := []model.PutRelayerQuoteRequest{ { OriginChainID: int(s.origin), OriginTokenAddr: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", @@ -55,7 +55,7 @@ func (s *QuoterSuite) TestGenerateQuotesForNativeToken() { expectedQuoteAmount := new(big.Int).Sub(balance, minGasToken) // Verify the quotes are generated as expected. - expectedQuotes := []model.PutQuoteRequest{ + expectedQuotes := []model.PutRelayerQuoteRequest{ { OriginChainID: int(s.origin), OriginTokenAddr: util.EthAddress.String(), @@ -82,7 +82,7 @@ func (s *QuoterSuite) TestGenerateQuotesForNativeToken() { expectedQuoteAmount = new(big.Int).Sub(balance, minGasToken) // Verify the quotes are generated as expected. - expectedQuotes = []model.PutQuoteRequest{ + expectedQuotes = []model.PutRelayerQuoteRequest{ { OriginChainID: int(s.origin), OriginTokenAddr: util.EthAddress.String(), diff --git a/services/rfq/relayer/relconfig/config.go b/services/rfq/relayer/relconfig/config.go index e55f87ac4e..a4449bf8db 100644 --- a/services/rfq/relayer/relconfig/config.go +++ b/services/rfq/relayer/relconfig/config.go @@ -33,8 +33,8 @@ type Config struct { BaseChainConfig ChainConfig `yaml:"base_chain_config"` // OmniRPCURL is the URL of the OmniRPC server. OmniRPCURL string `yaml:"omnirpc_url"` - // RfqAPIURL is the URL of the RFQ API. - RfqAPIURL string `yaml:"rfq_url"` + // RFQAPIURL is the URL of the RFQ API. + RFQAPIURL string `yaml:"rfq_url"` // RelayerAPIPort is the port of the relayer API. RelayerAPIPort string `yaml:"relayer_api_port"` // Database is the database config. @@ -67,6 +67,8 @@ type Config struct { SubmitSingleQuotes bool `yaml:"submit_single_quotes"` // VolumeLimit is the maximum dollar value of relayed transactions in the BlockWindow. VolumeLimit float64 `yaml:"volume_limit"` + // SupportActiveQuoting enables support for active quoting. + SupportActiveQuoting bool `yaml:"support_active_quoting"` } // ChainConfig represents the configuration for a chain. diff --git a/services/rfq/relayer/relconfig/getters.go b/services/rfq/relayer/relconfig/getters.go index d11534556c..2cb4880712 100644 --- a/services/rfq/relayer/relconfig/getters.go +++ b/services/rfq/relayer/relconfig/getters.go @@ -541,9 +541,9 @@ func (c Config) GetOmniRPCURL() string { return c.OmniRPCURL } -// GetRfqAPIURL returns the RFQ API URL. -func (c Config) GetRfqAPIURL() string { - return c.RfqAPIURL +// GetRFQAPIURL returns the RFQ API URL. +func (c Config) GetRFQAPIURL() string { + return c.RFQAPIURL } // GetDatabase returns the database config. diff --git a/services/rfq/relayer/service/relayer.go b/services/rfq/relayer/service/relayer.go index 90ad1a4e0e..38fdc4b701 100644 --- a/services/rfq/relayer/service/relayer.go +++ b/services/rfq/relayer/service/relayer.go @@ -130,7 +130,7 @@ func NewRelayer(ctx context.Context, metricHandler metrics.Handler, cfg relconfi priceFetcher := pricer.NewCoingeckoPriceFetcher(cfg.GetHTTPTimeout()) fp := pricer.NewFeePricer(cfg, omniClient, priceFetcher, metricHandler) - apiClient, err := rfqAPIClient.NewAuthenticatedClient(metricHandler, cfg.GetRfqAPIURL(), sg) + apiClient, err := rfqAPIClient.NewAuthenticatedClient(metricHandler, cfg.GetRFQAPIURL(), sg) if err != nil { return nil, fmt.Errorf("error creating RFQ API client: %w", err) } @@ -219,6 +219,16 @@ func (r *Relayer) Start(ctx context.Context) (err error) { } }) + if r.cfg.SupportActiveQuoting { + g.Go(func() error { + err = r.quoter.SubscribeActiveRFQ(ctx) + if err != nil { + return fmt.Errorf("could not subscribe to active RFQ: %w", err) + } + return nil + }) + } + g.Go(func() error { for { select { diff --git a/yarn.lock b/yarn.lock index 7c64ab096d..e8daf537c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,32 @@ # yarn lockfile v1 +"@actions/core@^1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.10.1.tgz#61108e7ac40acae95ee36da074fa5850ca4ced8a" + integrity sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g== + dependencies: + "@actions/http-client" "^2.0.1" + uuid "^8.3.2" + +"@actions/github@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@actions/github/-/github-6.0.0.tgz#65883433f9d81521b782a64cc1fd45eef2191ea7" + integrity sha512-alScpSVnYmjNEXboZjarjukQEzgCRmjMv6Xj47fsdnqGS73bjJNDpiiXmp8jr0UZLdUB6d9jW63IcmddUP+l0g== + dependencies: + "@actions/http-client" "^2.2.0" + "@octokit/core" "^5.0.1" + "@octokit/plugin-paginate-rest" "^9.0.0" + "@octokit/plugin-rest-endpoint-methods" "^10.0.0" + +"@actions/http-client@^2.0.1", "@actions/http-client@^2.2.0": + version "2.2.3" + resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-2.2.3.tgz#31fc0b25c0e665754ed39a9f19a8611fc6dab674" + integrity sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA== + dependencies: + tunnel "^0.0.6" + undici "^5.25.4" + "@adobe/css-tools@^4.0.1": version "4.4.0" resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.0.tgz#728c484f4e10df03d5a3acd0d8adcbbebff8ad63" @@ -322,7 +348,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.14.0", "@babel/core@^7.19.6", "@babel/core@^7.20.12", "@babel/core@^7.20.7", "@babel/core@^7.21.3", "@babel/core@^7.22.9", "@babel/core@^7.23.3", "@babel/core@^7.23.9", "@babel/core@^7.25.2", "@babel/core@^7.4.4", "@babel/core@^7.7.5": +"@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.14.0", "@babel/core@^7.20.12", "@babel/core@^7.20.7", "@babel/core@^7.21.3", "@babel/core@^7.22.9", "@babel/core@^7.23.3", "@babel/core@^7.23.9", "@babel/core@^7.25.2", "@babel/core@^7.4.4", "@babel/core@^7.7.5": 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== @@ -568,7 +594,7 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.11.5", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.8", "@babel/parser@^7.20.7", "@babel/parser@^7.22.7", "@babel/parser@^7.23.0", "@babel/parser@^7.23.9", "@babel/parser@^7.25.0", "@babel/parser@^7.25.3", "@babel/parser@^7.25.6", "@babel/parser@^7.7.0": +"@babel/parser@^7.1.0", "@babel/parser@^7.11.5", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.8", "@babel/parser@^7.20.7", "@babel/parser@^7.23.0", "@babel/parser@^7.23.9", "@babel/parser@^7.25.0", "@babel/parser@^7.25.3", "@babel/parser@^7.25.6", "@babel/parser@^7.7.0": version "7.25.6" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.25.6.tgz#85660c5ef388cbbf6e3d2a694ee97a38f18afe2f" integrity sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q== @@ -1194,7 +1220,7 @@ 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": +"@babel/plugin-transform-react-constant-elements@^7.21.3": version "7.25.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.25.1.tgz#71a665ed16ce618067d05f4a98130207349d82ae" integrity sha512-SLV/giH/V4SmloZ6Dt40HjTGTAIkxn33TVIHxNGNvo8ezMhrxBkzisj4op1KZYPIOHFLqhv60OHvX+YRu4xbmQ== @@ -1339,7 +1365,7 @@ "@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", "@babel/preset-env@^7.19.4", "@babel/preset-env@^7.20.2", "@babel/preset-env@^7.22.9", "@babel/preset-env@^7.25.4": +"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.11", "@babel/preset-env@^7.20.2", "@babel/preset-env@^7.22.9", "@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== @@ -1458,7 +1484,7 @@ "@babel/plugin-transform-react-jsx-development" "^7.24.7" "@babel/plugin-transform-react-pure-annotations" "^7.24.7" -"@babel/preset-typescript@^7.12.7", "@babel/preset-typescript@^7.18.6", "@babel/preset-typescript@^7.21.0", "@babel/preset-typescript@^7.22.5", "@babel/preset-typescript@^7.24.7": +"@babel/preset-typescript@^7.12.7", "@babel/preset-typescript@^7.21.0", "@babel/preset-typescript@^7.22.5", "@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== @@ -1523,7 +1549,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.22.8", "@babel/traverse@^7.23.2", "@babel/traverse@^7.24.7", "@babel/traverse@^7.24.8", "@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", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2": +"@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.22.8", "@babel/traverse@^7.23.2", "@babel/traverse@^7.24.7", "@babel/traverse@^7.24.8", "@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", "@babel/traverse@^7.7.0": version "7.25.6" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.6.tgz#04fad980e444f182ecf1520504941940a90fea41" integrity sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ== @@ -1536,7 +1562,7 @@ 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.0", "@babel/types@^7.20.7", "@babel/types@^7.21.3", "@babel/types@^7.24.7", "@babel/types@^7.24.8", "@babel/types@^7.25.0", "@babel/types@^7.25.2", "@babel/types@^7.25.6", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.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.21.3", "@babel/types@^7.24.7", "@babel/types@^7.24.8", "@babel/types@^7.25.0", "@babel/types@^7.25.2", "@babel/types@^7.25.6", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": version "7.25.6" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.6.tgz#893942ddb858f32ae7a004ec9d3a76b3463ef8e6" integrity sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw== @@ -1827,6 +1853,18 @@ unplugin "^1.10.1" zod "^3.22.4" +"@codecov/bundler-plugin-core@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@codecov/bundler-plugin-core/-/bundler-plugin-core-1.2.0.tgz#4a896dd3bd9f9f98a60519aadcf8e3daff37ae5d" + integrity sha512-ublUP5V0tW6oDnaJ1UBWvEmVAkvMmPNEwWkpF+WwJSCBWNLvWrkSwG84S3Gt5Xbnh17xEyAxXBmNzF+mXVXBgw== + dependencies: + "@actions/core" "^1.10.1" + "@actions/github" "^6.0.0" + chalk "4.1.2" + semver "^7.5.4" + unplugin "^1.10.1" + zod "^3.22.4" + "@codecov/rollup-plugin@^0.0.1-beta.10": version "0.0.1-beta.12" resolved "https://registry.yarnpkg.com/@codecov/rollup-plugin/-/rollup-plugin-0.0.1-beta.12.tgz#addf8e7689a7a387817911ac3c0ad4c26980bd7b" @@ -1835,6 +1873,14 @@ "@codecov/bundler-plugin-core" "^0.0.1-beta.12" unplugin "^1.10.1" +"@codecov/rollup-plugin@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@codecov/rollup-plugin/-/rollup-plugin-1.2.0.tgz#407f757010b4f3fab3f123a5f598b20ec837426f" + integrity sha512-4QgGHwvZ2mL4XOLf4jVz1bG9vyTT7+vCQ6d4Qi1+ttboMjNabmw5XO4WfSOwj+6kwuid8VQJmEF8/hJ6aZuc3w== + dependencies: + "@codecov/bundler-plugin-core" "^1.2.0" + unplugin "^1.10.1" + "@codecov/webpack-plugin@^0.0.1-beta.10": version "0.0.1-beta.12" resolved "https://registry.yarnpkg.com/@codecov/webpack-plugin/-/webpack-plugin-0.0.1-beta.12.tgz#ea067e60b31f7168d99b26fae5c5d1ac80669d64" @@ -1972,7 +2018,7 @@ "@devtools-ds/themes" "^1.2.1" clsx "1.1.0" -"@discoveryjs/json-ext@0.5.7", "@discoveryjs/json-ext@^0.5.0", "@discoveryjs/json-ext@^0.5.3": +"@discoveryjs/json-ext@0.5.7", "@discoveryjs/json-ext@^0.5.3": version "0.5.7" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== @@ -1992,81 +2038,6 @@ "@docsearch/css" "3.6.1" algoliasearch "^4.19.1" -"@docusaurus/core@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-3.1.1.tgz#29ce8df7a3d3d12ee8962d6d86133b87235ff17b" - integrity sha512-2nQfKFcf+MLEM7JXsXwQxPOmQAR6ytKMZVSx7tVi9HEm9WtfwBH1fp6bn8Gj4zLUhjWKCLoysQ9/Wm+EZCQ4yQ== - dependencies: - "@babel/core" "^7.23.3" - "@babel/generator" "^7.23.3" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-transform-runtime" "^7.22.9" - "@babel/preset-env" "^7.22.9" - "@babel/preset-react" "^7.22.5" - "@babel/preset-typescript" "^7.22.5" - "@babel/runtime" "^7.22.6" - "@babel/runtime-corejs3" "^7.22.6" - "@babel/traverse" "^7.22.8" - "@docusaurus/cssnano-preset" "3.1.1" - "@docusaurus/logger" "3.1.1" - "@docusaurus/mdx-loader" "3.1.1" - "@docusaurus/react-loadable" "5.5.2" - "@docusaurus/utils" "3.1.1" - "@docusaurus/utils-common" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" - "@slorber/static-site-generator-webpack-plugin" "^4.0.7" - "@svgr/webpack" "^6.5.1" - autoprefixer "^10.4.14" - babel-loader "^9.1.3" - babel-plugin-dynamic-import-node "^2.3.3" - boxen "^6.2.1" - chalk "^4.1.2" - chokidar "^3.5.3" - clean-css "^5.3.2" - cli-table3 "^0.6.3" - combine-promises "^1.1.0" - commander "^5.1.0" - copy-webpack-plugin "^11.0.0" - core-js "^3.31.1" - css-loader "^6.8.1" - css-minimizer-webpack-plugin "^4.2.2" - cssnano "^5.1.15" - del "^6.1.1" - detect-port "^1.5.1" - escape-html "^1.0.3" - eta "^2.2.0" - file-loader "^6.2.0" - fs-extra "^11.1.1" - html-minifier-terser "^7.2.0" - html-tags "^3.3.1" - html-webpack-plugin "^5.5.3" - leven "^3.1.0" - lodash "^4.17.21" - mini-css-extract-plugin "^2.7.6" - postcss "^8.4.26" - postcss-loader "^7.3.3" - prompts "^2.4.2" - react-dev-utils "^12.0.1" - react-helmet-async "^1.3.0" - react-loadable "npm:@docusaurus/react-loadable@5.5.2" - react-loadable-ssr-addon-v5-slorber "^1.0.1" - react-router "^5.3.4" - react-router-config "^5.1.1" - react-router-dom "^5.3.4" - rtl-detect "^1.0.4" - semver "^7.5.4" - serve-handler "^6.1.5" - shelljs "^0.8.5" - terser-webpack-plugin "^5.3.9" - tslib "^2.6.0" - update-notifier "^6.0.2" - url-loader "^4.1.1" - webpack "^5.88.1" - webpack-bundle-analyzer "^4.9.0" - webpack-dev-server "^4.15.1" - webpack-merge "^5.9.0" - webpackbar "^5.0.2" - "@docusaurus/core@3.5.2": version "3.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-3.5.2.tgz#3adedb90e7b6104592f1231043bd6bf91680c39c" @@ -2141,16 +2112,6 @@ webpack-merge "^5.9.0" webpackbar "^5.0.2" -"@docusaurus/cssnano-preset@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-3.1.1.tgz#03a4cb8e6d41654d7ff5ed79fddd73fd224feea4" - integrity sha512-LnoIDjJWbirdbVZDMq+4hwmrTl2yHDnBf9MLG9qyExeAE3ac35s4yUhJI8yyTCdixzNfKit4cbXblzzqMu4+8g== - dependencies: - cssnano-preset-advanced "^5.3.10" - postcss "^8.4.26" - postcss-sort-media-queries "^4.4.1" - tslib "^2.6.0" - "@docusaurus/cssnano-preset@3.5.2": version "3.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-3.5.2.tgz#6c1f2b2f9656f978c4694c84ab24592b04dcfab3" @@ -2161,14 +2122,6 @@ postcss-sort-media-queries "^5.2.0" tslib "^2.6.0" -"@docusaurus/logger@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-3.1.1.tgz#423e8270c00a57b1b3a0cc8a3ee0a4c522a68387" - integrity sha512-BjkNDpQzewcTnST8trx4idSoAla6zZ3w22NqM/UMcFtvYJgmoE4layuTzlfql3VFPNuivvj7BOExa/+21y4X2Q== - dependencies: - chalk "^4.1.2" - tslib "^2.6.0" - "@docusaurus/logger@3.5.2": version "3.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-3.5.2.tgz#1150339ad56844b30734115c19c580f3b25cf5ed" @@ -2177,38 +2130,6 @@ chalk "^4.1.2" tslib "^2.6.0" -"@docusaurus/mdx-loader@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-3.1.1.tgz#f79290abc5044bef1d7ecac4eccec887058b8e03" - integrity sha512-xN2IccH9+sv7TmxwsDJNS97BHdmlqWwho+kIVY4tcCXkp+k4QuzvWBeunIMzeayY4Fu13A6sAjHGv5qm72KyGA== - dependencies: - "@babel/parser" "^7.22.7" - "@babel/traverse" "^7.22.8" - "@docusaurus/logger" "3.1.1" - "@docusaurus/utils" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" - "@mdx-js/mdx" "^3.0.0" - "@slorber/remark-comment" "^1.0.0" - escape-html "^1.0.3" - estree-util-value-to-estree "^3.0.1" - file-loader "^6.2.0" - fs-extra "^11.1.1" - image-size "^1.0.2" - mdast-util-mdx "^3.0.0" - mdast-util-to-string "^4.0.0" - rehype-raw "^7.0.0" - remark-directive "^3.0.0" - remark-emoji "^4.0.0" - remark-frontmatter "^5.0.0" - remark-gfm "^4.0.0" - stringify-object "^3.3.0" - tslib "^2.6.0" - unified "^11.0.3" - unist-util-visit "^5.0.0" - url-loader "^4.1.1" - vfile "^6.0.1" - webpack "^5.88.1" - "@docusaurus/mdx-loader@3.5.2": version "3.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-3.5.2.tgz#99781641372c5037bcbe09bb8ade93a0e0ada57d" @@ -2239,34 +2160,6 @@ vfile "^6.0.1" webpack "^5.88.1" -"@docusaurus/module-type-aliases@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-3.1.1.tgz#b304402b0535a13ebd4c0db1c368d2604d54d02f" - integrity sha512-xBJyx0TMfAfVZ9ZeIOb1awdXgR4YJMocIEzTps91rq+hJDFJgJaylDtmoRhUxkwuYmNK1GJpW95b7DLztSBJ3A== - dependencies: - "@docusaurus/react-loadable" "5.5.2" - "@docusaurus/types" "3.1.1" - "@types/history" "^4.7.11" - "@types/react" "*" - "@types/react-router-config" "*" - "@types/react-router-dom" "*" - react-helmet-async "*" - react-loadable "npm:@docusaurus/react-loadable@5.5.2" - -"@docusaurus/module-type-aliases@3.2.1": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-3.2.1.tgz#fa8fd746890825b4301db2ddbe29d7cfbeee0380" - integrity sha512-FyViV5TqhL1vsM7eh29nJ5NtbRE6Ra6LP1PDcPvhwPSlA7eiWGRKAn3jWwMUcmjkos5SYY+sr0/feCdbM3eQHQ== - dependencies: - "@docusaurus/react-loadable" "5.5.2" - "@docusaurus/types" "3.2.1" - "@types/history" "^4.7.11" - "@types/react" "*" - "@types/react-router-config" "*" - "@types/react-router-dom" "*" - react-helmet-async "*" - react-loadable "npm:@docusaurus/react-loadable@5.5.2" - "@docusaurus/module-type-aliases@3.5.2": version "3.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-3.5.2.tgz#4e8f9c0703e23b2e07ebfce96598ec83e4dd2a9e" @@ -2280,19 +2173,20 @@ react-helmet-async "*" react-loadable "npm:@docusaurus/react-loadable@6.0.0" -"@docusaurus/plugin-content-blog@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.1.1.tgz#16f4fd723227b2158461bba6b9bcc18c1926f7ea" - integrity sha512-ew/3VtVoG3emoAKmoZl7oKe1zdFOsI0NbcHS26kIxt2Z8vcXKCUgK9jJJrz0TbOipyETPhqwq4nbitrY3baibg== - dependencies: - "@docusaurus/core" "3.1.1" - "@docusaurus/logger" "3.1.1" - "@docusaurus/mdx-loader" "3.1.1" - "@docusaurus/types" "3.1.1" - "@docusaurus/utils" "3.1.1" - "@docusaurus/utils-common" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" - cheerio "^1.0.0-rc.12" +"@docusaurus/plugin-content-blog@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.5.2.tgz#649c07c34da7603645f152bcebdf75285baed16b" + integrity sha512-R7ghWnMvjSf+aeNDH0K4fjyQnt5L0KzUEnUhmf1e3jZrv3wogeytZNN6n7X8yHcMsuZHPOrctQhXWnmxu+IRRg== + dependencies: + "@docusaurus/core" "3.5.2" + "@docusaurus/logger" "3.5.2" + "@docusaurus/mdx-loader" "3.5.2" + "@docusaurus/theme-common" "3.5.2" + "@docusaurus/types" "3.5.2" + "@docusaurus/utils" "3.5.2" + "@docusaurus/utils-common" "3.5.2" + "@docusaurus/utils-validation" "3.5.2" + cheerio "1.0.0-rc.12" feed "^4.2.2" fs-extra "^11.1.1" lodash "^4.17.21" @@ -2303,28 +2197,7 @@ utility-types "^3.10.0" webpack "^5.88.1" -"@docusaurus/plugin-content-docs@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.1.1.tgz#f2eddebf351dd8dd504a2c26061165c519e1f964" - integrity sha512-lhFq4E874zw0UOH7ujzxnCayOyAt0f9YPVYSb9ohxrdCM8B4szxitUw9rIX4V9JLLHVoqIJb6k+lJJ1jrcGJ0A== - dependencies: - "@docusaurus/core" "3.1.1" - "@docusaurus/logger" "3.1.1" - "@docusaurus/mdx-loader" "3.1.1" - "@docusaurus/module-type-aliases" "3.1.1" - "@docusaurus/types" "3.1.1" - "@docusaurus/utils" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" - "@types/react-router-config" "^5.0.7" - combine-promises "^1.1.0" - fs-extra "^11.1.1" - js-yaml "^4.1.0" - lodash "^4.17.21" - tslib "^2.6.0" - utility-types "^3.10.0" - webpack "^5.88.1" - -"@docusaurus/plugin-content-docs@^3.0.1": +"@docusaurus/plugin-content-docs@3.5.2", "@docusaurus/plugin-content-docs@^3.0.1": version "3.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.5.2.tgz#adcf6c0bd9a9818eb192ab831e0069ee62d31505" integrity sha512-Bt+OXn/CPtVqM3Di44vHjE7rPCEsRCB/DMo2qoOuozB9f7+lsdrHvD0QCHdBs0uhz6deYJDppAr2VgqybKPlVQ== @@ -2347,126 +2220,118 @@ utility-types "^3.10.0" webpack "^5.88.1" -"@docusaurus/plugin-content-pages@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.1.1.tgz#05aec68c2abeac2140c7a16d4c5b506bf4d19fb2" - integrity sha512-NQHncNRAJbyLtgTim9GlEnNYsFhuCxaCNkMwikuxLTiGIPH7r/jpb7O3f3jUMYMebZZZrDq5S7om9a6rvB/YCA== - dependencies: - "@docusaurus/core" "3.1.1" - "@docusaurus/mdx-loader" "3.1.1" - "@docusaurus/types" "3.1.1" - "@docusaurus/utils" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" +"@docusaurus/plugin-content-pages@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.5.2.tgz#2b59e43f5bc5b5176ff01835de706f1c65c2e68b" + integrity sha512-WzhHjNpoQAUz/ueO10cnundRz+VUtkjFhhaQ9jApyv1a46FPURO4cef89pyNIOMny1fjDz/NUN2z6Yi+5WUrCw== + dependencies: + "@docusaurus/core" "3.5.2" + "@docusaurus/mdx-loader" "3.5.2" + "@docusaurus/types" "3.5.2" + "@docusaurus/utils" "3.5.2" + "@docusaurus/utils-validation" "3.5.2" fs-extra "^11.1.1" tslib "^2.6.0" webpack "^5.88.1" -"@docusaurus/plugin-debug@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-3.1.1.tgz#cee5aae1fef288fb93f68894db79a2612e313d3f" - integrity sha512-xWeMkueM9wE/8LVvl4+Qf1WqwXmreMjI5Kgr7GYCDoJ8zu4kD+KaMhrh7py7MNM38IFvU1RfrGKacCEe2DRRfQ== +"@docusaurus/plugin-debug@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-3.5.2.tgz#c25ca6a59e62a17c797b367173fe80c06fdf2f65" + integrity sha512-kBK6GlN0itCkrmHuCS6aX1wmoWc5wpd5KJlqQ1FyrF0cLDnvsYSnh7+ftdwzt7G6lGBho8lrVwkkL9/iQvaSOA== dependencies: - "@docusaurus/core" "3.1.1" - "@docusaurus/types" "3.1.1" - "@docusaurus/utils" "3.1.1" + "@docusaurus/core" "3.5.2" + "@docusaurus/types" "3.5.2" + "@docusaurus/utils" "3.5.2" fs-extra "^11.1.1" react-json-view-lite "^1.2.0" tslib "^2.6.0" -"@docusaurus/plugin-google-analytics@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.1.1.tgz#bfc58205b4fcaf3222e04f9c3542f3bef9804887" - integrity sha512-+q2UpWTqVi8GdlLoSlD5bS/YpxW+QMoBwrPrUH/NpvpuOi0Of7MTotsQf9JWd3hymZxl2uu1o3PIrbpxfeDFDQ== +"@docusaurus/plugin-google-analytics@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.5.2.tgz#1143e78d1461d3c74a2746f036d25b18d4a2608d" + integrity sha512-rjEkJH/tJ8OXRE9bwhV2mb/WP93V441rD6XnM6MIluu7rk8qg38iSxS43ga2V2Q/2ib53PcqbDEJDG/yWQRJhQ== dependencies: - "@docusaurus/core" "3.1.1" - "@docusaurus/types" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" + "@docusaurus/core" "3.5.2" + "@docusaurus/types" "3.5.2" + "@docusaurus/utils-validation" "3.5.2" tslib "^2.6.0" -"@docusaurus/plugin-google-gtag@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.1.1.tgz#7e8b5aa6847a12461c104a65a335f4a45dae2f28" - integrity sha512-0mMPiBBlQ5LFHTtjxuvt/6yzh8v7OxLi3CbeEsxXZpUzcKO/GC7UA1VOWUoBeQzQL508J12HTAlR3IBU9OofSw== +"@docusaurus/plugin-google-gtag@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.5.2.tgz#60b5a9e1888c4fa16933f7c5cb5f2f2c31caad3a" + integrity sha512-lm8XL3xLkTPHFKKjLjEEAHUrW0SZBSHBE1I+i/tmYMBsjCcUB5UJ52geS5PSiOCFVR74tbPGcPHEV/gaaxFeSA== dependencies: - "@docusaurus/core" "3.1.1" - "@docusaurus/types" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" + "@docusaurus/core" "3.5.2" + "@docusaurus/types" "3.5.2" + "@docusaurus/utils-validation" "3.5.2" "@types/gtag.js" "^0.0.12" tslib "^2.6.0" -"@docusaurus/plugin-google-tag-manager@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.1.1.tgz#e1aae4d821e786d133386b4ae6e6fe66a4bc0089" - integrity sha512-d07bsrMLdDIryDtY17DgqYUbjkswZQr8cLWl4tzXrt5OR/T/zxC1SYKajzB3fd87zTu5W5klV5GmUwcNSMXQXA== +"@docusaurus/plugin-google-tag-manager@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.5.2.tgz#7a37334d2e7f00914d61ad05bc09391c4db3bfda" + integrity sha512-QkpX68PMOMu10Mvgvr5CfZAzZQFx8WLlOiUQ/Qmmcl6mjGK6H21WLT5x7xDmcpCoKA/3CegsqIqBR+nA137lQg== dependencies: - "@docusaurus/core" "3.1.1" - "@docusaurus/types" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" + "@docusaurus/core" "3.5.2" + "@docusaurus/types" "3.5.2" + "@docusaurus/utils-validation" "3.5.2" tslib "^2.6.0" -"@docusaurus/plugin-sitemap@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.1.1.tgz#8828bf5e2922273aad207a35189f22913e6a0dfd" - integrity sha512-iJ4hCaMmDaUqRv131XJdt/C/jJQx8UreDWTRqZKtNydvZVh/o4yXGRRFOplea1D9b/zpwL1Y+ZDwX7xMhIOTmg== - dependencies: - "@docusaurus/core" "3.1.1" - "@docusaurus/logger" "3.1.1" - "@docusaurus/types" "3.1.1" - "@docusaurus/utils" "3.1.1" - "@docusaurus/utils-common" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" +"@docusaurus/plugin-sitemap@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.5.2.tgz#9c940b27f3461c54d65295cf4c52cb20538bd360" + integrity sha512-DnlqYyRAdQ4NHY28TfHuVk414ft2uruP4QWCH//jzpHjqvKyXjj2fmDtI8RPUBh9K8iZKFMHRnLtzJKySPWvFA== + dependencies: + "@docusaurus/core" "3.5.2" + "@docusaurus/logger" "3.5.2" + "@docusaurus/types" "3.5.2" + "@docusaurus/utils" "3.5.2" + "@docusaurus/utils-common" "3.5.2" + "@docusaurus/utils-validation" "3.5.2" fs-extra "^11.1.1" sitemap "^7.1.1" tslib "^2.6.0" -"@docusaurus/preset-classic@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-3.1.1.tgz#15fd80012529dafd7e01cc0bce59d39ee6ad6bf5" - integrity sha512-jG4ys/hWYf69iaN/xOmF+3kjs4Nnz1Ay3CjFLDtYa8KdxbmUhArA9HmP26ru5N0wbVWhY+6kmpYhTJpez5wTyg== - dependencies: - "@docusaurus/core" "3.1.1" - "@docusaurus/plugin-content-blog" "3.1.1" - "@docusaurus/plugin-content-docs" "3.1.1" - "@docusaurus/plugin-content-pages" "3.1.1" - "@docusaurus/plugin-debug" "3.1.1" - "@docusaurus/plugin-google-analytics" "3.1.1" - "@docusaurus/plugin-google-gtag" "3.1.1" - "@docusaurus/plugin-google-tag-manager" "3.1.1" - "@docusaurus/plugin-sitemap" "3.1.1" - "@docusaurus/theme-classic" "3.1.1" - "@docusaurus/theme-common" "3.1.1" - "@docusaurus/theme-search-algolia" "3.1.1" - "@docusaurus/types" "3.1.1" - -"@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2": - version "5.5.2" - resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce" - integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ== +"@docusaurus/preset-classic@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-3.5.2.tgz#977f78510bbc556aa0539149eef960bb7ab52bd9" + integrity sha512-3ihfXQ95aOHiLB5uCu+9PRy2gZCeSZoDcqpnDvf3B+sTrMvMTr8qRUzBvWkoIqc82yG5prCboRjk1SVILKx6sg== dependencies: - "@types/react" "*" - prop-types "^15.6.2" + "@docusaurus/core" "3.5.2" + "@docusaurus/plugin-content-blog" "3.5.2" + "@docusaurus/plugin-content-docs" "3.5.2" + "@docusaurus/plugin-content-pages" "3.5.2" + "@docusaurus/plugin-debug" "3.5.2" + "@docusaurus/plugin-google-analytics" "3.5.2" + "@docusaurus/plugin-google-gtag" "3.5.2" + "@docusaurus/plugin-google-tag-manager" "3.5.2" + "@docusaurus/plugin-sitemap" "3.5.2" + "@docusaurus/theme-classic" "3.5.2" + "@docusaurus/theme-common" "3.5.2" + "@docusaurus/theme-search-algolia" "3.5.2" + "@docusaurus/types" "3.5.2" -"@docusaurus/theme-classic@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-3.1.1.tgz#0a188c787fc4bf2bb525cc30c7aa34e555ee96b8" - integrity sha512-GiPE/jbWM8Qv1A14lk6s9fhc0LhPEQ00eIczRO4QL2nAQJZXkjPG6zaVx+1cZxPFWbAsqSjKe2lqkwF3fGkQ7Q== - dependencies: - "@docusaurus/core" "3.1.1" - "@docusaurus/mdx-loader" "3.1.1" - "@docusaurus/module-type-aliases" "3.1.1" - "@docusaurus/plugin-content-blog" "3.1.1" - "@docusaurus/plugin-content-docs" "3.1.1" - "@docusaurus/plugin-content-pages" "3.1.1" - "@docusaurus/theme-common" "3.1.1" - "@docusaurus/theme-translations" "3.1.1" - "@docusaurus/types" "3.1.1" - "@docusaurus/utils" "3.1.1" - "@docusaurus/utils-common" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" +"@docusaurus/theme-classic@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-3.5.2.tgz#602ddb63d987ab1f939e3760c67bc1880f01c000" + integrity sha512-XRpinSix3NBv95Rk7xeMF9k4safMkwnpSgThn0UNQNumKvmcIYjfkwfh2BhwYh/BxMXQHJ/PdmNh22TQFpIaYg== + dependencies: + "@docusaurus/core" "3.5.2" + "@docusaurus/mdx-loader" "3.5.2" + "@docusaurus/module-type-aliases" "3.5.2" + "@docusaurus/plugin-content-blog" "3.5.2" + "@docusaurus/plugin-content-docs" "3.5.2" + "@docusaurus/plugin-content-pages" "3.5.2" + "@docusaurus/theme-common" "3.5.2" + "@docusaurus/theme-translations" "3.5.2" + "@docusaurus/types" "3.5.2" + "@docusaurus/utils" "3.5.2" + "@docusaurus/utils-common" "3.5.2" + "@docusaurus/utils-validation" "3.5.2" "@mdx-js/react" "^3.0.0" clsx "^2.0.0" copy-text-to-clipboard "^3.2.0" - infima "0.2.0-alpha.43" + infima "0.2.0-alpha.44" lodash "^4.17.21" nprogress "^0.2.0" postcss "^8.4.26" @@ -2477,27 +2342,6 @@ tslib "^2.6.0" utility-types "^3.10.0" -"@docusaurus/theme-common@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-3.1.1.tgz#5a16893928b8379c9e83aef01d753e7e142459e2" - integrity sha512-38urZfeMhN70YaXkwIGXmcUcv2CEYK/2l4b05GkJPrbEbgpsIZM3Xc+Js2ehBGGZmfZq8GjjQ5RNQYG+MYzCYg== - dependencies: - "@docusaurus/mdx-loader" "3.1.1" - "@docusaurus/module-type-aliases" "3.1.1" - "@docusaurus/plugin-content-blog" "3.1.1" - "@docusaurus/plugin-content-docs" "3.1.1" - "@docusaurus/plugin-content-pages" "3.1.1" - "@docusaurus/utils" "3.1.1" - "@docusaurus/utils-common" "3.1.1" - "@types/history" "^4.7.11" - "@types/react" "*" - "@types/react-router-config" "*" - clsx "^2.0.0" - parse-numeric-range "^1.3.0" - prism-react-renderer "^2.3.0" - tslib "^2.6.0" - utility-types "^3.10.0" - "@docusaurus/theme-common@3.5.2", "@docusaurus/theme-common@^3.0.1": version "3.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-3.5.2.tgz#b507ab869a1fba0be9c3c9d74f2f3d74c3ac78b2" @@ -2516,19 +2360,19 @@ tslib "^2.6.0" utility-types "^3.10.0" -"@docusaurus/theme-search-algolia@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.1.1.tgz#5170cd68cc59d150416b070bdc6d15c363ddf5e1" - integrity sha512-tBH9VY5EpRctVdaAhT+b1BY8y5dyHVZGFXyCHgTrvcXQy5CV4q7serEX7U3SveNT9zksmchPyct6i1sFDC4Z5g== +"@docusaurus/theme-search-algolia@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.5.2.tgz#466c83ca7e8017d95ae6889ccddc5ef8bf6b61c6" + integrity sha512-qW53kp3VzMnEqZGjakaV90sst3iN1o32PH+nawv1uepROO8aEGxptcq2R5rsv7aBShSRbZwIobdvSYKsZ5pqvA== dependencies: "@docsearch/react" "^3.5.2" - "@docusaurus/core" "3.1.1" - "@docusaurus/logger" "3.1.1" - "@docusaurus/plugin-content-docs" "3.1.1" - "@docusaurus/theme-common" "3.1.1" - "@docusaurus/theme-translations" "3.1.1" - "@docusaurus/utils" "3.1.1" - "@docusaurus/utils-validation" "3.1.1" + "@docusaurus/core" "3.5.2" + "@docusaurus/logger" "3.5.2" + "@docusaurus/plugin-content-docs" "3.5.2" + "@docusaurus/theme-common" "3.5.2" + "@docusaurus/theme-translations" "3.5.2" + "@docusaurus/utils" "3.5.2" + "@docusaurus/utils-validation" "3.5.2" algoliasearch "^4.18.0" algoliasearch-helper "^3.13.3" clsx "^2.0.0" @@ -2538,48 +2382,18 @@ tslib "^2.6.0" utility-types "^3.10.0" -"@docusaurus/theme-translations@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-3.1.1.tgz#117e91ba5e3a8178cb59f3028bf41de165a508c1" - integrity sha512-xvWQFwjxHphpJq5fgk37FXCDdAa2o+r7FX8IpMg+bGZBNXyWBu3MjZ+G4+eUVNpDhVinTc+j6ucL0Ain5KCGrg== +"@docusaurus/theme-translations@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-3.5.2.tgz#38f9ebf2a5d860397022206a05fef66c08863c89" + integrity sha512-GPZLcu4aT1EmqSTmbdpVrDENGR2yObFEX8ssEFYTCiAIVc0EihNSdOIBTazUvgNqwvnoU1A8vIs1xyzc3LITTw== dependencies: fs-extra "^11.1.1" tslib "^2.6.0" -"@docusaurus/tsconfig@3.2.1": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@docusaurus/tsconfig/-/tsconfig-3.2.1.tgz#6bdc0cb46414d09c7334d632b6d5e5472e6eb5a7" - integrity sha512-+biUwtsYW3oChLxYezzA+NIgS3Q9KDRl7add/YT54RXs9Q4rKInebxdHdG6JFs5BaTg45gyjDu0rvNVcGeHODg== - -"@docusaurus/types@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-3.1.1.tgz#747c9dee8cf7c3b0e5ee7351bac5e9c4fdc7f259" - integrity sha512-grBqOLnubUecgKFXN9q3uit2HFbCxTWX4Fam3ZFbMN0sWX9wOcDoA7lwdX/8AmeL20Oc4kQvWVgNrsT8bKRvzg== - dependencies: - "@mdx-js/mdx" "^3.0.0" - "@types/history" "^4.7.11" - "@types/react" "*" - commander "^5.1.0" - joi "^17.9.2" - react-helmet-async "^1.3.0" - utility-types "^3.10.0" - webpack "^5.88.1" - webpack-merge "^5.9.0" - -"@docusaurus/types@3.2.1": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-3.2.1.tgz#88ccd4b8fa236628a29c89b8b0f60f0cc4716b69" - integrity sha512-n/toxBzL2oxTtRTOFiGKsHypzn/Pm+sXyw+VSk1UbqbXQiHOwHwts55bpKwbcUgA530Is6kix3ELiFOv9GAMfw== - dependencies: - "@mdx-js/mdx" "^3.0.0" - "@types/history" "^4.7.11" - "@types/react" "*" - commander "^5.1.0" - joi "^17.9.2" - react-helmet-async "^1.3.0" - utility-types "^3.10.0" - webpack "^5.88.1" - webpack-merge "^5.9.0" +"@docusaurus/tsconfig@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/tsconfig/-/tsconfig-3.5.2.tgz#98878103ba217bff355cd8944926d9ca06e6e153" + integrity sha512-rQ7toURCFnWAIn8ubcquDs0ewhPwviMzxh6WpRjBW7sJVCXb6yzwUaY3HMNa0VXCFw+qkIbFywrMTf+Pb4uHWQ== "@docusaurus/types@3.5.2": version "3.5.2" @@ -2596,13 +2410,6 @@ webpack "^5.88.1" webpack-merge "^5.9.0" -"@docusaurus/utils-common@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-3.1.1.tgz#b48fade63523fd40f3adb67b47c3371e5183c20b" - integrity sha512-eGne3olsIoNfPug5ixjepZAIxeYFzHHnor55Wb2P57jNbtVaFvij/T+MS8U0dtZRFi50QU+UPmRrXdVUM8uyMg== - dependencies: - tslib "^2.6.0" - "@docusaurus/utils-common@3.5.2": version "3.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-3.5.2.tgz#4d7f5e962fbca3e2239d80457aa0e4bd3d8f7e0a" @@ -2610,17 +2417,6 @@ dependencies: tslib "^2.6.0" -"@docusaurus/utils-validation@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-3.1.1.tgz#3a747349ed05aee0e4d543552b41f3c9467ee731" - integrity sha512-KlY4P9YVDnwL+nExvlIpu79abfEv6ZCHuOX4ZQ+gtip+Wxj0daccdReIWWtqxM/Fb5Cz1nQvUCc7VEtT8IBUAA== - dependencies: - "@docusaurus/logger" "3.1.1" - "@docusaurus/utils" "3.1.1" - joi "^17.9.2" - js-yaml "^4.1.0" - tslib "^2.6.0" - "@docusaurus/utils-validation@3.5.2", "@docusaurus/utils-validation@^3.0.1": version "3.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-3.5.2.tgz#1b2b2f02082781cc8ce713d4c85e88d6d2fc4eb3" @@ -2635,29 +2431,6 @@ lodash "^4.17.21" tslib "^2.6.0" -"@docusaurus/utils@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-3.1.1.tgz#e822d14704e4b3bb451ca464a7cc56aea9b55a45" - integrity sha512-ZJfJa5cJQtRYtqijsPEnAZoduW6sjAQ7ZCWSZavLcV10Fw0Z3gSaPKA/B4micvj2afRZ4gZxT7KfYqe5H8Cetg== - dependencies: - "@docusaurus/logger" "3.1.1" - "@svgr/webpack" "^6.5.1" - escape-string-regexp "^4.0.0" - file-loader "^6.2.0" - fs-extra "^11.1.1" - github-slugger "^1.5.0" - globby "^11.1.0" - gray-matter "^4.0.3" - jiti "^1.20.0" - js-yaml "^4.1.0" - lodash "^4.17.21" - micromatch "^4.0.5" - resolve-pathname "^3.0.0" - shelljs "^0.8.5" - tslib "^2.6.0" - url-loader "^4.1.1" - webpack "^5.88.1" - "@docusaurus/utils@3.5.2", "@docusaurus/utils@^3.0.1": version "3.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-3.5.2.tgz#17763130215f18d7269025903588ef7fb373e2cb" @@ -4383,18 +4156,6 @@ jest-util "^25.5.0" slash "^3.0.0" -"@jest/console@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-28.1.3.tgz#2030606ec03a18c31803b8a36382762e447655df" - integrity sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw== - dependencies: - "@jest/types" "^28.1.3" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - slash "^3.0.0" - "@jest/console@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" @@ -4441,41 +4202,6 @@ slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/core@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-28.1.3.tgz#0ebf2bd39840f1233cd5f2d1e6fc8b71bd5a1ac7" - integrity sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA== - dependencies: - "@jest/console" "^28.1.3" - "@jest/reporters" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - ci-info "^3.2.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-changed-files "^28.1.3" - jest-config "^28.1.3" - jest-haste-map "^28.1.3" - jest-message-util "^28.1.3" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-resolve-dependencies "^28.1.3" - jest-runner "^28.1.3" - jest-runtime "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" - jest-watcher "^28.1.3" - micromatch "^4.0.4" - pretty-format "^28.1.3" - rimraf "^3.0.0" - slash "^3.0.0" - strip-ansi "^6.0.0" - "@jest/core@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" @@ -4519,16 +4245,6 @@ "@jest/types" "^25.5.0" jest-mock "^25.5.0" -"@jest/environment@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-28.1.3.tgz#abed43a6b040a4c24fdcb69eab1f97589b2d663e" - integrity sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA== - dependencies: - "@jest/fake-timers" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - jest-mock "^28.1.3" - "@jest/environment@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" @@ -4539,13 +4255,6 @@ "@types/node" "*" jest-mock "^29.7.0" -"@jest/expect-utils@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525" - integrity sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA== - dependencies: - jest-get-type "^28.0.2" - "@jest/expect-utils@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" @@ -4553,14 +4262,6 @@ dependencies: jest-get-type "^29.6.3" -"@jest/expect@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-28.1.3.tgz#9ac57e1d4491baca550f6bdbd232487177ad6a72" - integrity sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw== - dependencies: - expect "^28.1.3" - jest-snapshot "^28.1.3" - "@jest/expect@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" @@ -4580,18 +4281,6 @@ jest-util "^25.5.0" lolex "^5.0.0" -"@jest/fake-timers@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-28.1.3.tgz#230255b3ad0a3d4978f1d06f70685baea91c640e" - integrity sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw== - dependencies: - "@jest/types" "^28.1.3" - "@sinonjs/fake-timers" "^9.1.2" - "@types/node" "*" - jest-message-util "^28.1.3" - jest-mock "^28.1.3" - jest-util "^28.1.3" - "@jest/fake-timers@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" @@ -4613,15 +4302,6 @@ "@jest/types" "^25.5.0" expect "^25.5.0" -"@jest/globals@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-28.1.3.tgz#a601d78ddc5fdef542728309894895b4a42dc333" - integrity sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA== - dependencies: - "@jest/environment" "^28.1.3" - "@jest/expect" "^28.1.3" - "@jest/types" "^28.1.3" - "@jest/globals@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" @@ -4664,37 +4344,6 @@ optionalDependencies: node-notifier "^6.0.0" -"@jest/reporters@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-28.1.3.tgz#9adf6d265edafc5fc4a434cfb31e2df5a67a369a" - integrity sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@jridgewell/trace-mapping" "^0.3.13" - "@types/node" "*" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^5.1.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - jest-worker "^28.1.3" - slash "^3.0.0" - string-length "^4.0.1" - strip-ansi "^6.0.0" - terminal-link "^2.0.0" - v8-to-istanbul "^9.0.1" - "@jest/reporters@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" @@ -4725,13 +4374,6 @@ strip-ansi "^6.0.0" v8-to-istanbul "^9.0.1" -"@jest/schemas@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" - integrity sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg== - dependencies: - "@sinclair/typebox" "^0.24.1" - "@jest/schemas@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" @@ -4748,15 +4390,6 @@ graceful-fs "^4.2.4" source-map "^0.6.0" -"@jest/source-map@^28.1.2": - version "28.1.2" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-28.1.2.tgz#7fe832b172b497d6663cdff6c13b0a920e139e24" - integrity sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww== - dependencies: - "@jridgewell/trace-mapping" "^0.3.13" - callsites "^3.0.0" - graceful-fs "^4.2.9" - "@jest/source-map@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" @@ -4776,16 +4409,6 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-result@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-28.1.3.tgz#5eae945fd9f4b8fcfce74d239e6f725b6bf076c5" - integrity sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg== - dependencies: - "@jest/console" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - "@jest/test-result@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" @@ -4807,16 +4430,6 @@ jest-runner "^25.5.4" jest-runtime "^25.5.4" -"@jest/test-sequencer@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz#9d0c283d906ac599c74bde464bc0d7e6a82886c3" - integrity sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw== - dependencies: - "@jest/test-result" "^28.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - slash "^3.0.0" - "@jest/test-sequencer@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" @@ -4870,27 +4483,6 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/transform@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-28.1.3.tgz#59d8098e50ab07950e0f2fc0fc7ec462371281b0" - integrity sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA== - dependencies: - "@babel/core" "^7.11.6" - "@jest/types" "^28.1.3" - "@jridgewell/trace-mapping" "^0.3.13" - babel-plugin-istanbul "^6.1.1" - chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - jest-regex-util "^28.0.2" - jest-util "^28.1.3" - micromatch "^4.0.4" - pirates "^4.0.4" - slash "^3.0.0" - write-file-atomic "^4.0.1" - "@jest/transform@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" @@ -4953,18 +4545,6 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jest/types@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b" - integrity sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ== - dependencies: - "@jest/schemas" "^28.1.3" - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^17.0.8" - chalk "^4.0.0" - "@jest/types@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" @@ -5017,7 +4597,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== @@ -6604,6 +6184,11 @@ dependencies: "@octokit/types" "^6.0.3" +"@octokit/auth-token@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-4.0.0.tgz#40d203ea827b9f17f42a29c6afb93b7745ef80c7" + integrity sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA== + "@octokit/core@^3.5.1": version "3.6.0" resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.6.0.tgz#3376cb9f3008d9b3d110370d90e0a1fcd5fe6085" @@ -6617,6 +6202,19 @@ before-after-hook "^2.2.0" universal-user-agent "^6.0.0" +"@octokit/core@^5.0.1": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-5.2.0.tgz#ddbeaefc6b44a39834e1bb2e58a49a117672a7ea" + integrity sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg== + dependencies: + "@octokit/auth-token" "^4.0.0" + "@octokit/graphql" "^7.1.0" + "@octokit/request" "^8.3.1" + "@octokit/request-error" "^5.1.0" + "@octokit/types" "^13.0.0" + before-after-hook "^2.2.0" + universal-user-agent "^6.0.0" + "@octokit/endpoint@^6.0.1": version "6.0.12" resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" @@ -6626,6 +6224,14 @@ is-plain-object "^5.0.0" universal-user-agent "^6.0.0" +"@octokit/endpoint@^9.0.1": + version "9.0.5" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-9.0.5.tgz#e6c0ee684e307614c02fc6ac12274c50da465c44" + integrity sha512-ekqR4/+PCLkEBF6qgj8WqJfvDq65RH85OAgrtnVp1mSxaXF03u2xW/hUdweGS5654IlC0wkNYC18Z50tSYTAFw== + dependencies: + "@octokit/types" "^13.1.0" + universal-user-agent "^6.0.0" + "@octokit/graphql@^4.5.8": version "4.8.0" resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.8.0.tgz#664d9b11c0e12112cbf78e10f49a05959aa22cc3" @@ -6635,11 +6241,30 @@ "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" +"@octokit/graphql@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-7.1.0.tgz#9bc1c5de92f026648131f04101cab949eeffe4e0" + integrity sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ== + dependencies: + "@octokit/request" "^8.3.0" + "@octokit/types" "^13.0.0" + universal-user-agent "^6.0.0" + "@octokit/openapi-types@^12.11.0": version "12.11.0" resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-12.11.0.tgz#da5638d64f2b919bca89ce6602d059f1b52d3ef0" integrity sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ== +"@octokit/openapi-types@^20.0.0": + version "20.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-20.0.0.tgz#9ec2daa0090eeb865ee147636e0c00f73790c6e5" + integrity sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA== + +"@octokit/openapi-types@^22.2.0": + version "22.2.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-22.2.0.tgz#75aa7dcd440821d99def6a60b5f014207ae4968e" + integrity sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg== + "@octokit/plugin-enterprise-rest@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" @@ -6652,11 +6277,25 @@ dependencies: "@octokit/types" "^6.40.0" +"@octokit/plugin-paginate-rest@^9.0.0": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.1.tgz#2e2a2f0f52c9a4b1da1a3aa17dabe3c459b9e401" + integrity sha512-wfGhE/TAkXZRLjksFXuDZdmGnJQHvtU/joFQdweXUgzo1XwvBCD4o4+75NtFfjfLK5IwLf9vHTfSiU3sLRYpRw== + dependencies: + "@octokit/types" "^12.6.0" + "@octokit/plugin-request-log@^1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== +"@octokit/plugin-rest-endpoint-methods@^10.0.0": + version "10.4.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz#41ba478a558b9f554793075b2e20cd2ef973be17" + integrity sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg== + dependencies: + "@octokit/types" "^12.6.0" + "@octokit/plugin-rest-endpoint-methods@^5.12.0": version "5.16.2" resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz#7ee8bf586df97dd6868cf68f641354e908c25342" @@ -6674,6 +6313,15 @@ deprecation "^2.0.0" once "^1.4.0" +"@octokit/request-error@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-5.1.0.tgz#ee4138538d08c81a60be3f320cd71063064a3b30" + integrity sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q== + dependencies: + "@octokit/types" "^13.1.0" + deprecation "^2.0.0" + once "^1.4.0" + "@octokit/request@^5.6.0", "@octokit/request@^5.6.3": version "5.6.3" resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.3.tgz#19a022515a5bba965ac06c9d1334514eb50c48b0" @@ -6686,6 +6334,16 @@ node-fetch "^2.6.7" universal-user-agent "^6.0.0" +"@octokit/request@^8.3.0", "@octokit/request@^8.3.1": + version "8.4.0" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-8.4.0.tgz#7f4b7b1daa3d1f48c0977ad8fffa2c18adef8974" + integrity sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw== + dependencies: + "@octokit/endpoint" "^9.0.1" + "@octokit/request-error" "^5.1.0" + "@octokit/types" "^13.1.0" + universal-user-agent "^6.0.0" + "@octokit/rest@^18.1.0": version "18.12.0" resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881" @@ -6696,6 +6354,20 @@ "@octokit/plugin-request-log" "^1.0.4" "@octokit/plugin-rest-endpoint-methods" "^5.12.0" +"@octokit/types@^12.6.0": + version "12.6.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-12.6.0.tgz#8100fb9eeedfe083aae66473bd97b15b62aedcb2" + integrity sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw== + dependencies: + "@octokit/openapi-types" "^20.0.0" + +"@octokit/types@^13.0.0", "@octokit/types@^13.1.0": + version "13.5.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-13.5.0.tgz#4796e56b7b267ebc7c921dcec262b3d5bfb18883" + integrity sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ== + dependencies: + "@octokit/openapi-types" "^22.2.0" + "@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.39.0", "@octokit/types@^6.40.0": version "6.41.0" resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.41.0.tgz#e58ef78d78596d2fb7df9c6259802464b5f84a04" @@ -6723,49 +6395,6 @@ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.0.1.tgz#93da90fc209a0a4ff09c1deb037fbb35e4020890" integrity sha512-yQJaT5HDp9hYOOp4jTYxMsR02gdFZFXhewX5HW9Jo4fsqSVqqyIO/xTHdWDaKX5a3pv1txmf076Lziz+sO7L1w== -"@paloaltonetworks/openapi-to-postmanv2@3.1.0-hotfix.1": - version "3.1.0-hotfix.1" - resolved "https://registry.yarnpkg.com/@paloaltonetworks/openapi-to-postmanv2/-/openapi-to-postmanv2-3.1.0-hotfix.1.tgz#4baf401d2e94ba86d888e6011a4c45d824e88114" - integrity sha512-0bdaPCEyQbnUo4xpOu7EzxXXkDx4BAXqc8QSbVBlzlVB5KoTLJiKKB4c3fa4BXbK+3u/OqfLbeNCebc2EC8ngA== - dependencies: - "@paloaltonetworks/postman-collection" "^4.1.0" - ajv "8.1.0" - ajv-formats "2.1.1" - async "3.2.1" - commander "2.20.3" - js-yaml "3.14.1" - json-schema-merge-allof "0.8.1" - lodash "4.17.21" - oas-resolver-browser "2.5.2" - path-browserify "1.0.1" - yaml "1.10.2" - -"@paloaltonetworks/postman-code-generators@1.1.15-patch.2": - version "1.1.15-patch.2" - resolved "https://registry.yarnpkg.com/@paloaltonetworks/postman-code-generators/-/postman-code-generators-1.1.15-patch.2.tgz#012051485269a2da6bd9a6b60031ddbc53e5e363" - integrity sha512-tRnAKtV4M8wLxcVnAx6ZCjCqbrR1xiqJNQkf1A71K8UxEP3N/+EspT82N5c0555w02oYFk21ViHuzuhm4gaGLw== - dependencies: - "@paloaltonetworks/postman-collection" "^4.1.0" - async "^3.2.4" - path "^0.12.7" - shelljs "^0.8.5" - -"@paloaltonetworks/postman-collection@^4.1.0": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@paloaltonetworks/postman-collection/-/postman-collection-4.1.1.tgz#b2130bc8d7396ea8e6a6b2e4642a6b224b41e1e1" - integrity sha512-9JHHkkD8Xb4rvdKob7TDPRfqfmdG3KU0aO5gJyyjvMFbOVysam5I0d8/9HPOuJXWkUHGo3Sn+ov2Fcm2bnJ52Q== - dependencies: - file-type "3.9.0" - http-reasons "0.1.0" - iconv-lite "0.6.3" - liquid-json "0.3.1" - lodash "4.17.21" - mime-format "2.0.1" - mime-types "2.1.34" - postman-url-encoder "3.0.5" - semver "7.3.5" - uuid "8.3.2" - "@parcel/watcher-android-arm64@2.4.1": version "2.4.1" resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz#c2c19a3c442313ff007d2d7a9c2c1dd3e1c9ca84" @@ -7084,6 +6713,19 @@ is-reference "1.2.1" magic-string "^0.30.3" +"@rollup/plugin-commonjs@^28.0.0": + version "28.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.0.tgz#44b5e49cb5d5e6233f1e4996013a8649fdbb9557" + integrity sha512-BJcu+a+Mpq476DMXG+hevgPSl56bkUoi88dKT8t3RyUp8kGuOh+2bU8Gs7zXDlu+fyZggnJ+iOBGrb/O1SorYg== + dependencies: + "@rollup/pluginutils" "^5.0.1" + commondir "^1.0.1" + estree-walker "^2.0.2" + fdir "^6.1.1" + is-reference "1.2.1" + magic-string "^0.30.3" + picomatch "^2.3.1" + "@rollup/plugin-json@^4.0.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3" @@ -7091,22 +6733,21 @@ dependencies: "@rollup/pluginutils" "^3.0.8" -"@rollup/plugin-json@^6.0.1": +"@rollup/plugin-json@^6.0.1", "@rollup/plugin-json@^6.1.0": version "6.1.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-6.1.0.tgz#fbe784e29682e9bb6dee28ea75a1a83702e7b805" integrity sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA== dependencies: "@rollup/pluginutils" "^5.1.0" -"@rollup/plugin-node-resolve@^15.2.3": - version "15.2.3" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz#e5e0b059bd85ca57489492f295ce88c2d4b0daf9" - integrity sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ== +"@rollup/plugin-node-resolve@^15.2.3", "@rollup/plugin-node-resolve@^15.3.0": + version "15.3.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz#efbb35515c9672e541c08d59caba2eff492a55d5" + integrity sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag== dependencies: "@rollup/pluginutils" "^5.0.1" "@types/resolve" "1.20.2" deepmerge "^4.2.2" - is-builtin-module "^3.2.1" is-module "^1.0.0" resolve "^1.22.1" @@ -7147,6 +6788,15 @@ "@rollup/pluginutils" "^5.1.0" resolve "^1.22.1" +"@rollup/plugin-url@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@rollup/plugin-url/-/plugin-url-8.0.2.tgz#aab4e209e9e012f65582bd99eb80b3bbdfe15afb" + integrity sha512-5yW2LP5NBEgkvIRSSEdJkmxe5cUNZKG3eenKtfJvSkxVm/xTTu7w+ayBtNwhozl1ZnTUCU0xFaRQR+cBl2H7TQ== + dependencies: + "@rollup/pluginutils" "^5.0.1" + make-dir "^3.1.0" + mime "^3.0.0" + "@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.0.9", "@rollup/pluginutils@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" @@ -7156,7 +6806,7 @@ estree-walker "^1.0.1" picomatch "^2.2.2" -"@rollup/pluginutils@^4.0.0": +"@rollup/pluginutils@^4.0.0", "@rollup/pluginutils@^4.1.2": version "4.2.1" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d" integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ== @@ -7165,9 +6815,9 @@ picomatch "^2.2.2" "@rollup/pluginutils@^5.0.1", "@rollup/pluginutils@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.0.tgz#7e53eddc8c7f483a4ad0b94afb1f7f5fd3c771e0" - integrity sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g== + version "5.1.2" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.2.tgz#d3bc9f0fea4fd4086aaac6aa102f3fa587ce8bd9" + integrity sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw== dependencies: "@types/estree" "^1.0.0" estree-walker "^2.0.2" @@ -7178,81 +6828,161 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.3.tgz#155c7d82c1b36c3ad84d9adf9b3cd520cba81a0f" integrity sha512-MmKSfaB9GX+zXl6E8z4koOr/xU63AMVleLEa64v7R0QF/ZloMs5vcD1sHgM64GXXS1csaJutG+ddtzcueI/BLg== +"@rollup/rollup-android-arm-eabi@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz#8b613b9725e8f9479d142970b106b6ae878610d5" + integrity sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w== + "@rollup/rollup-android-arm64@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.3.tgz#b94b6fa002bd94a9cbd8f9e47e23b25e5bd113ba" integrity sha512-zrt8ecH07PE3sB4jPOggweBjJMzI1JG5xI2DIsUbkA+7K+Gkjys6eV7i9pOenNSDJH3eOr/jLb/PzqtmdwDq5g== +"@rollup/rollup-android-arm64@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz#654ca1049189132ff602bfcf8df14c18da1f15fb" + integrity sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA== + "@rollup/rollup-darwin-arm64@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.3.tgz#0934126cf9cbeadfe0eb7471ab5d1517e8cd8dcc" integrity sha512-P0UxIOrKNBFTQaXTxOH4RxuEBVCgEA5UTNV6Yz7z9QHnUJ7eLX9reOd/NYMO3+XZO2cco19mXTxDMXxit4R/eQ== +"@rollup/rollup-darwin-arm64@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz#6d241d099d1518ef0c2205d96b3fa52e0fe1954b" + integrity sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q== + "@rollup/rollup-darwin-x64@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.3.tgz#0ce8e1e0f349778938c7c90e4bdc730640e0a13e" integrity sha512-L1M0vKGO5ASKntqtsFEjTq/fD91vAqnzeaF6sfNAy55aD+Hi2pBI5DKwCO+UNDQHWsDViJLqshxOahXyLSh3EA== +"@rollup/rollup-darwin-x64@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz#42bd19d292a57ee11734c980c4650de26b457791" + integrity sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw== + "@rollup/rollup-linux-arm-gnueabihf@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.3.tgz#5669d34775ad5d71e4f29ade99d0ff4df523afb6" integrity sha512-btVgIsCjuYFKUjopPoWiDqmoUXQDiW2A4C3Mtmp5vACm7/GnyuprqIDPNczeyR5W8rTXEbkmrJux7cJmD99D2g== +"@rollup/rollup-linux-arm-gnueabihf@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz#f23555ee3d8fe941c5c5fd458cd22b65eb1c2232" + integrity sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ== + "@rollup/rollup-linux-arm-musleabihf@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.3.tgz#f6d1a0e1da4061370cb2f4244fbdd727c806dd88" integrity sha512-zmjbSphplZlau6ZTkxd3+NMtE4UKVy7U4aVFMmHcgO5CUbw17ZP6QCgyxhzGaU/wFFdTfiojjbLG3/0p9HhAqA== +"@rollup/rollup-linux-arm-musleabihf@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz#f3bbd1ae2420f5539d40ac1fde2b38da67779baa" + integrity sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg== + "@rollup/rollup-linux-arm64-gnu@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.3.tgz#ed96a05e99743dee4d23cc4913fc6e01a0089c88" integrity sha512-nSZfcZtAnQPRZmUkUQwZq2OjQciR6tEoJaZVFvLHsj0MF6QhNMg0fQ6mUOsiCUpTqxTx0/O6gX0V/nYc7LrgPw== +"@rollup/rollup-linux-arm64-gnu@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz#7abe900120113e08a1f90afb84c7c28774054d15" + integrity sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw== + "@rollup/rollup-linux-arm64-musl@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.3.tgz#057ea26eaa7e537a06ded617d23d57eab3cecb58" integrity sha512-MnvSPGO8KJXIMGlQDYfvYS3IosFN2rKsvxRpPO2l2cum+Z3exiExLwVU+GExL96pn8IP+GdH8Tz70EpBhO0sIQ== +"@rollup/rollup-linux-arm64-musl@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz#9e655285c8175cd44f57d6a1e8e5dedfbba1d820" + integrity sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA== + "@rollup/rollup-linux-powerpc64le-gnu@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.3.tgz#6e6e1f9404c9bf3fbd7d51cd11cd288a9a2843aa" integrity sha512-+W+p/9QNDr2vE2AXU0qIy0qQE75E8RTwTwgqS2G5CRQ11vzq0tbnfBd6brWhS9bCRjAjepJe2fvvkvS3dno+iw== +"@rollup/rollup-linux-powerpc64le-gnu@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz#9a79ae6c9e9d8fe83d49e2712ecf4302db5bef5e" + integrity sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg== + "@rollup/rollup-linux-riscv64-gnu@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.3.tgz#eef1536a53f6e6658a2a778130e6b1a4a41cb439" integrity sha512-yXH6K6KfqGXaxHrtr+Uoy+JpNlUlI46BKVyonGiaD74ravdnF9BUNC+vV+SIuB96hUMGShhKV693rF9QDfO6nQ== +"@rollup/rollup-linux-riscv64-gnu@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz#67ac70eca4ace8e2942fabca95164e8874ab8128" + integrity sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA== + "@rollup/rollup-linux-s390x-gnu@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.3.tgz#2b28fb89ca084efaf8086f435025d96b4a966957" integrity sha512-R8cwY9wcnApN/KDYWTH4gV/ypvy9yZUHlbJvfaiXSB48JO3KpwSpjOGqO4jnGkLDSk1hgjYkTbTt6Q7uvPf8eg== +"@rollup/rollup-linux-s390x-gnu@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz#9f883a7440f51a22ed7f99e1d070bd84ea5005fc" + integrity sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q== + "@rollup/rollup-linux-x64-gnu@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.3.tgz#5226cde6c6b495b04a3392c1d2c572844e42f06b" integrity sha512-kZPbX/NOPh0vhS5sI+dR8L1bU2cSO9FgxwM8r7wHzGydzfSjLRCFAT87GR5U9scj2rhzN3JPYVC7NoBbl4FZ0g== +"@rollup/rollup-linux-x64-gnu@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz#70116ae6c577fe367f58559e2cffb5641a1dd9d0" + integrity sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg== + "@rollup/rollup-linux-x64-musl@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.3.tgz#2c2412982e6c2a00a2ecac6d548ebb02f0aa6ca4" integrity sha512-S0Yq+xA1VEH66uiMNhijsWAafffydd2X5b77eLHfRmfLsRSpbiAWiRHV6DEpz6aOToPsgid7TI9rGd6zB1rhbg== +"@rollup/rollup-linux-x64-musl@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz#f473f88219feb07b0b98b53a7923be716d1d182f" + integrity sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g== + "@rollup/rollup-win32-arm64-msvc@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.3.tgz#fbb6ef5379199e2ec0103ef32877b0985c773a55" integrity sha512-9isNzeL34yquCPyerog+IMCNxKR8XYmGd0tHSV+OVx0TmE0aJOo9uw4fZfUuk2qxobP5sug6vNdZR6u7Mw7Q+Q== +"@rollup/rollup-win32-arm64-msvc@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz#4349482d17f5d1c58604d1c8900540d676f420e0" + integrity sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw== + "@rollup/rollup-win32-ia32-msvc@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.3.tgz#d50e2082e147e24d87fe34abbf6246525ec3845a" integrity sha512-nMIdKnfZfzn1Vsk+RuOvl43ONTZXoAPUUxgcU0tXooqg4YrAqzfKzVenqqk2g5efWh46/D28cKFrOzDSW28gTA== +"@rollup/rollup-win32-ia32-msvc@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz#a6fc39a15db618040ec3c2a24c1e26cb5f4d7422" + integrity sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g== + "@rollup/rollup-win32-x64-msvc@4.21.3": version "4.21.3" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.3.tgz#4115233aa1bd5a2060214f96d8511f6247093212" integrity sha512-fOvu7PCQjAj4eWDEuD8Xz5gpzFqXzGlxHZozHP4b9Jxv9APtdxL6STqztDzMLuRXEc4UpXGGhx029Xgm91QBeA== +"@rollup/rollup-win32-x64-msvc@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz#3dd5d53e900df2a40841882c02e56f866c04d202" + integrity sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q== + "@rtk-query/graphql-request-base-query@^2.2.0": version "2.3.1" resolved "https://registry.yarnpkg.com/@rtk-query/graphql-request-base-query/-/graphql-request-base-query-2.3.1.tgz#edfb2873705c4c6b05b171b6233c590de4572739" @@ -7507,11 +7237,6 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== -"@sinclair/typebox@^0.24.1": - version "0.24.51" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" - integrity sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA== - "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -7522,11 +7247,6 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== -"@sindresorhus/is@^0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" - integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== - "@sindresorhus/is@^4.0.0", "@sindresorhus/is@^4.6.0": version "4.6.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" @@ -7558,13 +7278,6 @@ dependencies: "@sinonjs/commons" "^3.0.0" -"@sinonjs/fake-timers@^9.1.2": - version "9.1.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" - integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== - dependencies: - "@sinonjs/commons" "^1.7.0" - "@slorber/remark-comment@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@slorber/remark-comment/-/remark-comment-1.0.0.tgz#2a020b3f4579c89dec0361673206c28d67e08f5a" @@ -7574,34 +7287,18 @@ micromark-util-character "^1.1.0" micromark-util-symbol "^1.0.1" -"@slorber/static-site-generator-webpack-plugin@^4.0.7": - version "4.0.7" - resolved "https://registry.yarnpkg.com/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.7.tgz#fc1678bddefab014e2145cbe25b3ce4e1cfc36f3" - integrity sha512-Ug7x6z5lwrz0WqdnNFOMYrDQNTPAprvHLSh6+/fmml3qUiz6l5eq+2MzLKWtn/q5K5NpSiFsZTP/fck/3vjSxA== - dependencies: - eval "^0.1.8" - p-map "^4.0.0" - webpack-sources "^3.2.2" - "@socket.io/component-emitter@~3.1.0": version "3.1.2" resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2" integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA== -"@solidity-parser/parser@^0.14.0", "@solidity-parser/parser@^0.14.5": +"@solidity-parser/parser@^0.14.0": version "0.14.5" resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.5.tgz#87bc3cc7b068e08195c219c91cd8ddff5ef1a804" integrity sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg== dependencies: antlr4ts "^0.5.0-alpha.4" -"@solidity-parser/parser@^0.16.0": - version "0.16.2" - resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.16.2.tgz#42cb1e3d88b3e8029b0c9befff00b634cd92d2fa" - integrity sha512-PI9NfoA3P8XK2VBkK5oIfRgKDsicwDZfkVq9ZTBCQYGOP1N2owgY2dyLGyU5/J/hQs8KRk55kdmvTLjy3Mu3vg== - dependencies: - antlr4ts "^0.5.0-alpha.4" - "@solidity-parser/parser@^0.18.0": version "0.18.0" resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.18.0.tgz#8e77a02a09ecce957255a2f48c9a7178ec191908" @@ -8684,17 +8381,12 @@ resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz#4001f5d5dd87fa13303e36ee106e3ff3a7eb8b22" integrity sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g== -"@svgr/babel-plugin-add-jsx-attribute@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz#74a5d648bd0347bda99d82409d87b8ca80b9a1ba" - integrity sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ== - -"@svgr/babel-plugin-remove-jsx-attribute@*", "@svgr/babel-plugin-remove-jsx-attribute@8.0.0": +"@svgr/babel-plugin-remove-jsx-attribute@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz#69177f7937233caca3a1afb051906698f2f59186" integrity sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA== -"@svgr/babel-plugin-remove-jsx-empty-expression@*", "@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0": +"@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz#c2c48104cfd7dcd557f373b70a56e9e3bdae1d44" integrity sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA== @@ -8704,51 +8396,26 @@ resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz#8fbb6b2e91fa26ac5d4aa25c6b6e4f20f9c0ae27" integrity sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ== -"@svgr/babel-plugin-replace-jsx-attribute-value@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz#fb9d22ea26d2bc5e0a44b763d4c46d5d3f596c60" - integrity sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg== - "@svgr/babel-plugin-svg-dynamic-title@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz#1d5ba1d281363fc0f2f29a60d6d936f9bbc657b0" integrity sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og== -"@svgr/babel-plugin-svg-dynamic-title@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz#01b2024a2b53ffaa5efceaa0bf3e1d5a4c520ce4" - integrity sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw== - "@svgr/babel-plugin-svg-em-dimensions@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz#35e08df300ea8b1d41cb8f62309c241b0369e501" integrity sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g== -"@svgr/babel-plugin-svg-em-dimensions@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz#dd3fa9f5b24eb4f93bcf121c3d40ff5facecb217" - integrity sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA== - "@svgr/babel-plugin-transform-react-native-svg@8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz#90a8b63998b688b284f255c6a5248abd5b28d754" integrity sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q== -"@svgr/babel-plugin-transform-react-native-svg@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz#1d8e945a03df65b601551097d8f5e34351d3d305" - integrity sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg== - "@svgr/babel-plugin-transform-svg-component@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz#013b4bfca88779711f0ed2739f3f7efcefcf4f7e" integrity sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw== -"@svgr/babel-plugin-transform-svg-component@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz#48620b9e590e25ff95a80f811544218d27f8a250" - integrity sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ== - "@svgr/babel-preset@8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-8.1.0.tgz#0e87119aecdf1c424840b9d4565b7137cabf9ece" @@ -8763,20 +8430,6 @@ "@svgr/babel-plugin-transform-react-native-svg" "8.1.0" "@svgr/babel-plugin-transform-svg-component" "8.0.0" -"@svgr/babel-preset@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-6.5.1.tgz#b90de7979c8843c5c580c7e2ec71f024b49eb828" - integrity sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw== - dependencies: - "@svgr/babel-plugin-add-jsx-attribute" "^6.5.1" - "@svgr/babel-plugin-remove-jsx-attribute" "*" - "@svgr/babel-plugin-remove-jsx-empty-expression" "*" - "@svgr/babel-plugin-replace-jsx-attribute-value" "^6.5.1" - "@svgr/babel-plugin-svg-dynamic-title" "^6.5.1" - "@svgr/babel-plugin-svg-em-dimensions" "^6.5.1" - "@svgr/babel-plugin-transform-react-native-svg" "^6.5.1" - "@svgr/babel-plugin-transform-svg-component" "^6.5.1" - "@svgr/core@8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@svgr/core/-/core-8.1.0.tgz#41146f9b40b1a10beaf5cc4f361a16a3c1885e88" @@ -8788,17 +8441,6 @@ cosmiconfig "^8.1.3" snake-case "^3.0.4" -"@svgr/core@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/core/-/core-6.5.1.tgz#d3e8aa9dbe3fbd747f9ee4282c1c77a27410488a" - integrity sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw== - dependencies: - "@babel/core" "^7.19.6" - "@svgr/babel-preset" "^6.5.1" - "@svgr/plugin-jsx" "^6.5.1" - camelcase "^6.2.0" - cosmiconfig "^7.0.1" - "@svgr/hast-util-to-babel-ast@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz#6952fd9ce0f470e1aded293b792a2705faf4ffd4" @@ -8807,14 +8449,6 @@ "@babel/types" "^7.21.3" entities "^4.4.0" -"@svgr/hast-util-to-babel-ast@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz#81800bd09b5bcdb968bf6ee7c863d2288fdb80d2" - integrity sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw== - dependencies: - "@babel/types" "^7.20.0" - entities "^4.4.0" - "@svgr/plugin-jsx@8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz#96969f04a24b58b174ee4cd974c60475acbd6928" @@ -8825,16 +8459,6 @@ "@svgr/hast-util-to-babel-ast" "8.0.0" svg-parser "^2.0.4" -"@svgr/plugin-jsx@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz#0e30d1878e771ca753c94e69581c7971542a7072" - integrity sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw== - dependencies: - "@babel/core" "^7.19.6" - "@svgr/babel-preset" "^6.5.1" - "@svgr/hast-util-to-babel-ast" "^6.5.1" - svg-parser "^2.0.4" - "@svgr/plugin-svgo@8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz#b115b7b967b564f89ac58feae89b88c3decd0f00" @@ -8844,29 +8468,6 @@ deepmerge "^4.3.1" svgo "^3.0.2" -"@svgr/plugin-svgo@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-6.5.1.tgz#0f91910e988fc0b842f88e0960c2862e022abe84" - integrity sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ== - dependencies: - cosmiconfig "^7.0.1" - deepmerge "^4.2.2" - svgo "^2.8.0" - -"@svgr/webpack@^6.5.1": - version "6.5.1" - resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-6.5.1.tgz#ecf027814fc1cb2decc29dc92f39c3cf691e40e8" - integrity sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA== - dependencies: - "@babel/core" "^7.19.6" - "@babel/plugin-transform-react-constant-elements" "^7.18.12" - "@babel/preset-env" "^7.19.4" - "@babel/preset-react" "^7.18.6" - "@babel/preset-typescript" "^7.18.6" - "@svgr/core" "^6.5.1" - "@svgr/plugin-jsx" "^6.5.1" - "@svgr/plugin-svgo" "^6.5.1" - "@svgr/webpack@^8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-8.1.0.tgz#16f1b5346f102f89fda6ec7338b96a701d8be0c2" @@ -8901,6 +8502,14 @@ "@swc/counter" "^0.1.3" tslib "^2.4.0" +"@synapsecns/coverage-aggregator@file:./packages/coverage-aggregator": + version "1.0.6" + dependencies: + glob "^8.0.3" + path "^0.12.7" + ts-jest "^29.0.5" + yargs "^17.6.2" + "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -9057,11 +8666,6 @@ traverse "^0.6.7" unified "^9.2.2" -"@tokenizer/token@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276" - integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A== - "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -9785,7 +9389,7 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ== -"@types/prettier@^2.1.1", "@types/prettier@^2.1.5": +"@types/prettier@^2.1.1": version "2.7.3" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== @@ -9823,9 +9427,9 @@ "@types/react" "*" "@types/react-redux@^7.1.20": - version "7.1.33" - resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.33.tgz#53c5564f03f1ded90904e3c90f77e4bd4dc20b15" - integrity sha512-NF8m5AjWCkert+fosDsN3hAlHzpjSiXlVy9EgQEmLoBhaNXbmyeGs/aj5dQzKuF+/q+S7JQagorGDW8pJ28Hmg== + version "7.1.34" + resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.34.tgz#83613e1957c481521e6776beeac4fd506d11bd0e" + integrity sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ== dependencies: "@types/hoist-non-react-statics" "^3.3.0" "@types/react" "*" @@ -9992,12 +9596,12 @@ "@types/methods" "^1.1.4" "@types/superagent" "^8.1.0" -"@types/swagger-jsdoc@^6.0.4": +"@types/swagger-jsdoc@6.0.4", "@types/swagger-jsdoc@^6.0.4": version "6.0.4" resolved "https://registry.yarnpkg.com/@types/swagger-jsdoc/-/swagger-jsdoc-6.0.4.tgz#bb4f60f3a5f103818e022f2e29ff8935113fb83d" integrity sha512-W+Xw5epcOZrF/AooUM/PccNMSAFOKWZA5dasNyMujTwsBkU74njSJBpvCCJhHAJ95XRMzQrrW844Btu0uoetwQ== -"@types/swagger-ui-express@^4.1.6": +"@types/swagger-ui-express@4.1.6", "@types/swagger-ui-express@^4.1.6": version "4.1.6" resolved "https://registry.yarnpkg.com/@types/swagger-ui-express/-/swagger-ui-express-4.1.6.tgz#d0929e3fabac1a96a8a9c6c7ee8d42362c5cdf48" integrity sha512-UVSiGYXa5IzdJJG3hrc86e8KdZWLYxyEsVoUI4iPXc7CO4VZ3AfNP8d/8+hrDRIqz+HAaSMtZSqAsF3Nq2X/Dg== @@ -11210,21 +10814,6 @@ "@webassemblyjs/wast-parser" "1.9.0" "@xtuc/long" "4.2.2" -"@webpack-cli/configtest@^2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646" - integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== - -"@webpack-cli/info@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd" - integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== - -"@webpack-cli/serve@^2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" - integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== - "@whatwg-node/events@^0.0.3": version "0.0.3" resolved "https://registry.yarnpkg.com/@whatwg-node/events/-/events-0.0.3.tgz#13a65dd4f5893f55280f766e29ae48074927acad" @@ -11428,7 +11017,7 @@ acorn-walk@^8.0.0, acorn-walk@^8.1.1, acorn-walk@^8.2.0: dependencies: acorn "^8.11.0" -acorn@^6.0.1, acorn@^6.0.7, acorn@^6.4.1: +acorn@^6.0.1, acorn@^6.4.1: version "6.4.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== @@ -11549,16 +11138,6 @@ ajv-keywords@^5.1.0: dependencies: fast-deep-equal "^3.1.3" -ajv@8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.1.0.tgz#45d5d3d36c7cdd808930cc3e603cf6200dbeb736" - integrity sha512-B/Sk2Ix7A36fs/ZkuGLIR86EdjbgR6fsAcbx9lOP/QBSXujDNbVmIS/U4Itz5k8fPFDeVZl/zQ/gJW4Jrq6XjQ== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - ajv@8.11.0: version "8.11.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" @@ -11579,7 +11158,7 @@ ajv@8.6.3: require-from-string "^2.0.2" uri-js "^4.2.2" -ajv@^6.0.0, ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.12.6, ajv@^6.6.1, ajv@^6.9.1: +ajv@^6.0.0, ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.12.6: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -11661,7 +11240,7 @@ ansi-colors@^4.1.1, ansi-colors@^4.1.3: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== -ansi-escapes@^3.0.0, ansi-escapes@^3.2.0: +ansi-escapes@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== @@ -11718,11 +11297,6 @@ ansi-sequence-parser@^1.1.0: resolved "https://registry.yarnpkg.com/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz#e0aa1cdcbc8f8bb0b5bca625aac41f5f056973cf" integrity sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg== -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA== - ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -11754,12 +11328,7 @@ ansi-to-html@^0.6.11: dependencies: entities "^2.0.0" -antlr4@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.7.1.tgz#69984014f096e9e775f53dd9744bf994d8959773" - integrity sha512-haHyTW7Y9joE5MVs37P2lNYfU2RWBLfcRDD8OWldcdZm5TiCE91B5Xl1oWSwiDUSd4rlExpt2pu1fksYQjRBYQ== - -antlr4@^4.11.0, antlr4@^4.13.1-patch-1: +antlr4@^4.13.1-patch-1: version "4.13.2" resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.13.2.tgz#0d084ad0e32620482a9c3a0e2470c02e72e4006d" integrity sha512-QiVbZhyy4xAZ17UPEuG3YTOt8ZaoeOR1CvEAqrEsDBsOqINslaB147i9xqljZqoyf5S+EUlGStaj+t22LT9MOg== @@ -11812,18 +11381,6 @@ aproba@^1.0.3, aproba@^1.1.1: resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== -arch@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" - integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== - -archive-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/archive-type/-/archive-type-4.0.0.tgz#f92e72233056dfc6969472749c267bdb046b1d70" - integrity sha512-zV4Ky0v1F8dBrdYElwTvQhweQ0P7Kwc1aluqJsYtOBP01jXcWCyW2IEfI1YiqsG+Iy7ZR+o5LF1N+PGECBxHWA== - dependencies: - file-type "^4.2.0" - archy@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" @@ -12161,7 +11718,7 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== -ast-parents@0.0.1, ast-parents@^0.0.1: +ast-parents@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/ast-parents/-/ast-parents-0.0.1.tgz#508fd0f05d0c48775d9eccda2e174423261e8dd3" integrity sha512-XHusKxKz3zoYk1ic8Un640joHbFMhbqneyoZfoKnEGtf2ey9Uh/IdpcQplODdO/kENaMIWsD0nJm4+wX3UNLHA== @@ -12235,17 +11792,17 @@ async@1.x: resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" integrity sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w== -async@3.2.1: - version "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.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.2.tgz#2eb7671034bb2194d45d30e31e24ec7e7f9670cd" + integrity sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g== async@3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== -async@^3.2.3, async@^3.2.4: +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== @@ -12293,7 +11850,7 @@ auto-bind@~4.0.0: resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" integrity sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ== -autoprefixer@^10.4.12, autoprefixer@^10.4.14, autoprefixer@^10.4.16, autoprefixer@^10.4.19, autoprefixer@^10.4.7: +autoprefixer@^10.4.14, autoprefixer@^10.4.16, autoprefixer@^10.4.19, autoprefixer@^10.4.7: version "10.4.20" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.20.tgz#5caec14d43976ef42e32dcb4bd62878e96be5b3b" integrity sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g== @@ -12380,19 +11937,6 @@ babel-jest@^25.2.6, babel-jest@^25.5.1: graceful-fs "^4.2.4" slash "^3.0.0" -babel-jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-28.1.3.tgz#c1187258197c099072156a0a121c11ee1e3917d5" - integrity sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q== - dependencies: - "@jest/transform" "^28.1.3" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^28.1.3" - chalk "^4.0.0" - graceful-fs "^4.2.9" - slash "^3.0.0" - babel-jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" @@ -12497,16 +12041,6 @@ babel-plugin-jest-hoist@^25.5.0: "@babel/types" "^7.3.3" "@types/babel__traverse" "^7.0.6" -babel-plugin-jest-hoist@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz#1952c4d0ea50f2d6d794353762278d1d8cca3fbe" - integrity sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q== - dependencies: - "@babel/template" "^7.3.3" - "@babel/types" "^7.3.3" - "@types/babel__core" "^7.1.14" - "@types/babel__traverse" "^7.0.6" - babel-plugin-jest-hoist@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" @@ -12702,14 +12236,6 @@ babel-preset-jest@^25.5.0: babel-plugin-jest-hoist "^25.5.0" babel-preset-current-node-syntax "^0.1.2" -babel-preset-jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz#5dfc20b99abed5db994406c2b9ab94c73aaa419d" - integrity sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A== - dependencies: - babel-plugin-jest-hoist "^28.1.3" - babel-preset-current-node-syntax "^1.0.0" - babel-preset-jest@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" @@ -12835,54 +12361,6 @@ bignumber@^1.1.0: resolved "https://registry.yarnpkg.com/bignumber/-/bignumber-1.1.0.tgz#e6ab0a743da5f3ea018e5c17597d121f7868c159" integrity sha512-EGqHCKkEAwVwufcEOCYhZQqdVH+7cNCyPZ9yxisYvSjHFB+d9YcGMvorsFpeN5IJpC+lC6K+FHhu8+S4MgJazw== -bin-build@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bin-build/-/bin-build-3.0.0.tgz#c5780a25a8a9f966d8244217e6c1f5082a143861" - integrity sha512-jcUOof71/TNAI2uM5uoUaDq2ePcVBQ3R/qhxAz1rX7UfvduAL/RXD3jXzvn8cVcDJdGVkiR1shal3OH0ImpuhA== - dependencies: - decompress "^4.0.0" - download "^6.2.2" - execa "^0.7.0" - p-map-series "^1.0.0" - tempfile "^2.0.0" - -bin-check@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/bin-check/-/bin-check-4.1.0.tgz#fc495970bdc88bb1d5a35fc17e65c4a149fc4a49" - integrity sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA== - dependencies: - execa "^0.7.0" - executable "^4.1.0" - -bin-version-check@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/bin-version-check/-/bin-version-check-4.0.0.tgz#7d819c62496991f80d893e6e02a3032361608f71" - integrity sha512-sR631OrhC+1f8Cvs8WyVWOA33Y8tgwjETNPyyD/myRBXLkfS/vl74FmH/lFcRl9KY3zwGh7jFhvyk9vV3/3ilQ== - dependencies: - bin-version "^3.0.0" - semver "^5.6.0" - semver-truncate "^1.1.2" - -bin-version@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/bin-version/-/bin-version-3.1.0.tgz#5b09eb280752b1bd28f0c9db3f96f2f43b6c0839" - integrity sha512-Mkfm4iE1VFt4xd4vH+gx+0/71esbfus2LsnCGe8Pi4mndSPyT+NGES/Eg99jx8/lUGWfu3z2yuB/bt5UB+iVbQ== - dependencies: - execa "^1.0.0" - find-versions "^3.0.0" - -bin-wrapper@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/bin-wrapper/-/bin-wrapper-4.1.0.tgz#99348f2cf85031e3ef7efce7e5300aeaae960605" - integrity sha512-hfRmo7hWIXPkbpi0ZltboCMVrU+0ClXR/JgbCKKjlDjQf6igXa7OwdqNcFWQZPZTgiY7ZpzE3+LjjkLiTN2T7Q== - dependencies: - bin-check "^4.1.0" - bin-version-check "^4.0.0" - download "^7.1.0" - import-lazy "^3.1.0" - os-filter-obj "^2.0.0" - pify "^4.0.1" - binary-extensions@^1.0.0: version "1.13.1" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" @@ -12905,14 +12383,6 @@ bintrees@1.0.2: resolved "https://registry.yarnpkg.com/bintrees/-/bintrees-1.0.2.tgz#49f896d6e858a4a499df85c38fb399b9aff840f8" integrity sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw== -bl@^1.0.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7" - integrity sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww== - dependencies: - readable-stream "^2.3.5" - safe-buffer "^5.1.1" - bl@^4.0.3, bl@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" @@ -13218,29 +12688,11 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" -buffer-alloc-unsafe@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" - integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== - -buffer-alloc@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" - integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== - dependencies: - buffer-alloc-unsafe "^1.1.0" - buffer-fill "^1.0.0" - buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== -buffer-fill@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" - integrity sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ== - buffer-from@1.x, buffer-from@^1.0.0, buffer-from@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -13265,7 +12717,7 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" -buffer@^5.0.5, buffer@^5.2.1, buffer@^5.5.0, buffer@^5.6.0: +buffer@^5.0.5, buffer@^5.5.0, buffer@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -13451,19 +12903,6 @@ cacheable-request@^10.2.8: normalize-url "^8.0.0" responselike "^3.0.0" -cacheable-request@^2.1.1: - version "2.1.4" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" - integrity sha512-vag0O2LKZ/najSoUwDbVlnlCFvhBE/7mGTY2B5FgCBDcRD+oVV1HYTOwM6JZfMg/hIcM6IwnTZ1uQQL5/X3xIQ== - dependencies: - clone-response "1.0.2" - get-stream "3.0.0" - http-cache-semantics "3.8.1" - keyv "3.0.0" - lowercase-keys "1.0.0" - normalize-url "2.0.1" - responselike "1.0.2" - cacheable-request@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" @@ -13516,30 +12955,11 @@ call-me-maybe@^1.0.1: resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.2.tgz#03f964f19522ba643b1b0693acb9152fe2074baa" integrity sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ== -caller-callsite@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" - integrity sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ== - dependencies: - callsites "^2.0.0" - -caller-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" - integrity sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A== - dependencies: - caller-callsite "^2.0.0" - callsite@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" integrity sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ== -callsites@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" - integrity sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ== - callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -13644,16 +13064,6 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== -caw@^2.0.0, caw@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/caw/-/caw-2.0.1.tgz#6c3ca071fc194720883c2dc5da9b074bfc7e9e95" - integrity sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA== - dependencies: - get-proxy "^2.0.0" - isurl "^1.0.0-alpha5" - tunnel-agent "^0.6.0" - url-to-options "^1.0.1" - "cbw-sdk@npm:@coinbase/wallet-sdk@3.9.3": version "3.9.3" resolved "https://registry.yarnpkg.com/@coinbase/wallet-sdk/-/wallet-sdk-3.9.3.tgz#daf10cb0c85d0363315b7270cb3f02bedc408aab" @@ -13700,17 +13110,6 @@ chalk@4.1.2, chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1. ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -13862,22 +13261,18 @@ cheerio-select@^2.1.0: domhandler "^5.0.3" domutils "^3.0.1" -cheerio@^1.0.0-rc.12: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0.tgz#1ede4895a82f26e8af71009f961a9b8cb60d6a81" - integrity sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww== +cheerio@1.0.0-rc.12: + version "1.0.0-rc.12" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.12.tgz#788bf7466506b1c6bf5fae51d24a2c4d62e47683" + integrity sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q== dependencies: cheerio-select "^2.1.0" dom-serializer "^2.0.0" domhandler "^5.0.3" - domutils "^3.1.0" - encoding-sniffer "^0.2.0" - htmlparser2 "^9.1.0" - parse5 "^7.1.2" + domutils "^3.0.1" + htmlparser2 "^8.0.1" + parse5 "^7.0.0" parse5-htmlparser2-tree-adapter "^7.0.0" - parse5-parser-stream "^7.1.2" - undici "^6.19.5" - whatwg-mimetype "^4.0.0" chokidar@3.3.1: version "3.3.1" @@ -14067,7 +13462,7 @@ cli-boxes@^3.0.0: resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-3.0.0.tgz#71a10c716feeba005e4504f36329ef0b17cf3145" integrity sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g== -cli-cursor@^2.0.0, cli-cursor@^2.1.0: +cli-cursor@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" integrity sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw== @@ -14123,11 +13518,6 @@ cli-truncate@^3.1.0: slice-ansi "^5.0.0" string-width "^5.0.0" -cli-width@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" - integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== - cli-width@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" @@ -14183,13 +13573,6 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" -clone-response@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q== - dependencies: - mimic-response "^1.0.0" - clone-response@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" @@ -14348,7 +13731,7 @@ colorette@^1.2.0, colorette@^1.2.2: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== -colorette@^2.0.10, colorette@^2.0.14, colorette@^2.0.16: +colorette@^2.0.10, colorette@^2.0.16: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== @@ -14408,12 +13791,7 @@ command-line-usage@^6.1.0: table-layout "^1.0.2" typical "^5.2.0" -commander@2.18.0: - version "2.18.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" - integrity sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ== - -commander@2.20.3, commander@^2.19.0, commander@^2.20.0, commander@^2.8.1: +commander@2.20.3, commander@^2.19.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -14428,7 +13806,7 @@ commander@6.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.0.tgz#b990bfb8ac030aedc6d11bc04d1488ffef56db75" integrity sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q== -commander@^10.0.0, commander@^10.0.1: +commander@^10.0.0: version "10.0.1" resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== @@ -14656,11 +14034,6 @@ console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control- resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== -console-stream@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/console-stream/-/console-stream-0.1.1.tgz#a095fe07b20465955f2fafd28b5d72bccd949d44" - integrity sha512-QC/8l9e6ofi6nqZ5PawlDgzmMw3OxIXtvolBzap/F4UDBJlDaZRSNbL/lb41C29FcbSJncBFlJFj2WJoNyZRfQ== - constant-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1" @@ -14680,7 +14053,7 @@ content-disposition@0.5.2: resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" integrity sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA== -content-disposition@0.5.4, content-disposition@^0.5.2: +content-disposition@0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== @@ -14925,16 +14298,6 @@ cors@^2.8.1: object-assign "^4" vary "^1" -cosmiconfig@^5.0.7: - version "5.2.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" - cosmiconfig@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" @@ -14946,7 +14309,7 @@ cosmiconfig@^6.0.0: path-type "^4.0.0" yaml "^1.7.2" -cosmiconfig@^7.0.0, cosmiconfig@^7.0.1, cosmiconfig@^7.1.0: +cosmiconfig@^7.0.0, cosmiconfig@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== @@ -15077,7 +14440,7 @@ cross-inspect@1.0.1: dependencies: tslib "^2.4.0" -cross-spawn@^5.0.1, cross-spawn@^5.1.0: +cross-spawn@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" integrity sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A== @@ -15199,18 +14562,6 @@ css-loader@^6.8.1: postcss-value-parser "^4.2.0" semver "^7.5.4" -css-minimizer-webpack-plugin@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz#79f6199eb5adf1ff7ba57f105e3752d15211eb35" - integrity sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA== - dependencies: - cssnano "^5.1.8" - jest-worker "^29.1.2" - postcss "^8.4.17" - schema-utils "^4.0.0" - serialize-javascript "^6.0.0" - source-map "^0.6.1" - css-minimizer-webpack-plugin@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz#33effe662edb1a0bf08ad633c32fa75d0f7ec565" @@ -15284,18 +14635,6 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== -cssnano-preset-advanced@^5.3.10: - version "5.3.10" - resolved "https://registry.yarnpkg.com/cssnano-preset-advanced/-/cssnano-preset-advanced-5.3.10.tgz#25558a1fbf3a871fb6429ce71e41be7f5aca6eef" - integrity sha512-fnYJyCS9jgMU+cmHO1rPSPf9axbQyD7iUhLO5Df6O4G+fKIOMps+ZbU0PdGFejFBBZ3Pftf18fn1eG7MAPUSWQ== - dependencies: - autoprefixer "^10.4.12" - cssnano-preset-default "^5.2.14" - postcss-discard-unused "^5.1.0" - postcss-merge-idents "^5.1.1" - postcss-reduce-idents "^5.2.0" - postcss-zindex "^5.1.0" - cssnano-preset-advanced@^6.1.2: version "6.1.2" resolved "https://registry.yarnpkg.com/cssnano-preset-advanced/-/cssnano-preset-advanced-6.1.2.tgz#82b090872b8f98c471f681d541c735acf8b94d3f" @@ -15390,7 +14729,7 @@ cssnano-utils@^4.0.2: resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-4.0.2.tgz#56f61c126cd0f11f2eef1596239d730d9fceff3c" integrity sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ== -cssnano@^5.0.1, cssnano@^5.1.15, cssnano@^5.1.8: +cssnano@^5.0.1: version "5.1.15" resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.15.tgz#ded66b5480d5127fcb44dac12ea5a983755136bf" integrity sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw== @@ -15782,7 +15121,7 @@ decode-uri-component@^0.2.0, decode-uri-component@^0.2.2: resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== -decompress-response@^3.2.0, decompress-response@^3.3.0: +decompress-response@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" integrity sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA== @@ -15796,59 +15135,6 @@ decompress-response@^6.0.0: dependencies: mimic-response "^3.1.0" -decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1" - integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ== - dependencies: - file-type "^5.2.0" - is-stream "^1.1.0" - tar-stream "^1.5.2" - -decompress-tarbz2@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b" - integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A== - dependencies: - decompress-tar "^4.1.0" - file-type "^6.1.0" - is-stream "^1.1.0" - seek-bzip "^1.0.5" - unbzip2-stream "^1.0.9" - -decompress-targz@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee" - integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w== - dependencies: - decompress-tar "^4.1.1" - file-type "^5.2.0" - is-stream "^1.1.0" - -decompress-unzip@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69" - integrity sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw== - dependencies: - file-type "^3.8.0" - get-stream "^2.2.0" - pify "^2.3.0" - yauzl "^2.4.2" - -decompress@^4.0.0, decompress@^4.2.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.1.tgz#007f55cc6a62c055afa37c07eb6a4ee1b773f118" - integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ== - dependencies: - decompress-tar "^4.0.0" - decompress-tarbz2 "^4.0.0" - decompress-targz "^4.0.0" - decompress-unzip "^4.0.1" - graceful-fs "^4.1.10" - make-dir "^1.0.0" - pify "^2.3.0" - strip-dirs "^2.0.0" - dedent@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" @@ -16165,6 +15451,13 @@ detect-node@^2.0.4: resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== +detect-package-manager@3.0.2, detect-package-manager@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/detect-package-manager/-/detect-package-manager-3.0.2.tgz#ca34261ab84198072580e93ae86582c575428da9" + integrity sha512-8JFjJHutStYrfWwzfretQoyNGoZVW1Fsrp4JO9spa7h/fBfwgTMEIy4/LBzRDGsxwVPHU0q+T9YvwLDJoOApLQ== + dependencies: + execa "^5.1.1" + detect-package-manager@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/detect-package-manager/-/detect-package-manager-2.0.1.tgz#6b182e3ae5e1826752bfef1de9a7b828cffa50d8" @@ -16172,13 +15465,6 @@ detect-package-manager@^2.0.1: dependencies: execa "^5.1.1" -detect-package-manager@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/detect-package-manager/-/detect-package-manager-3.0.2.tgz#ca34261ab84198072580e93ae86582c575428da9" - integrity sha512-8JFjJHutStYrfWwzfretQoyNGoZVW1Fsrp4JO9spa7h/fBfwgTMEIy4/LBzRDGsxwVPHU0q+T9YvwLDJoOApLQ== - dependencies: - execa "^5.1.1" - detect-port-alt@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" @@ -16225,11 +15511,6 @@ diff-sequences@^25.2.6: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd" integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg== -diff-sequences@^28.1.1: - version "28.1.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6" - integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw== - diff-sequences@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" @@ -16321,34 +15602,10 @@ doctrine@^2.1.0: dependencies: esutils "^2.0.2" -docusaurus-plugin-openapi-docs@3.0.0-beta.10: - version "3.0.0-beta.10" - resolved "https://registry.yarnpkg.com/docusaurus-plugin-openapi-docs/-/docusaurus-plugin-openapi-docs-3.0.0-beta.10.tgz#f0c303ee852487c852c163d019678a92b53abf81" - integrity sha512-BtMBH4TzCiMM0WbO2ZAMXSuL7Ge9yyASZhAycb5vX+KSUnUgp47/Ex2f6/evBfaadnr6vXYEr1UBT1fSiJYh5w== - dependencies: - "@apidevtools/json-schema-ref-parser" "^11.5.4" - "@docusaurus/plugin-content-docs" "^3.0.1" - "@docusaurus/utils" "^3.0.1" - "@docusaurus/utils-validation" "^3.0.1" - "@paloaltonetworks/openapi-to-postmanv2" "3.1.0-hotfix.1" - "@paloaltonetworks/postman-collection" "^4.1.0" - "@redocly/openapi-core" "^1.10.5" - chalk "^4.1.2" - clsx "^1.1.1" - fs-extra "^9.0.1" - json-pointer "^0.6.2" - json-schema-merge-allof "^0.8.1" - json5 "^2.2.3" - lodash "^4.17.20" - mustache "^4.2.0" - slugify "^1.6.5" - swagger2openapi "^7.0.8" - xml-formatter "^2.6.1" - -docusaurus-plugin-openapi-docs@^3.0.0-beta.10: - version "3.0.2" - resolved "https://registry.yarnpkg.com/docusaurus-plugin-openapi-docs/-/docusaurus-plugin-openapi-docs-3.0.2.tgz#45f45ed187b04127edc682f33a2c56c725fc0f7b" - integrity sha512-58aYOOD6VhJpACvzSdI2fSFbX9H9PA7Xt+O5TfNRMyl5XuSIyrVhqfLr1VqTcBkc4xqA5MGko7JLBIxymXxPvg== +docusaurus-plugin-openapi-docs@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/docusaurus-plugin-openapi-docs/-/docusaurus-plugin-openapi-docs-4.0.1.tgz#2c767cd7af363b24413f7249e85b26ac154d803a" + integrity sha512-ST0VLbRMTNz2O0NFIezWcF0dNYrGf34/oUmn3wH3hdMcStGQIOCEwD3JvuzyQ7WygjAR8md2kITHeRBRB2yhAA== dependencies: "@apidevtools/json-schema-ref-parser" "^11.5.4" "@docusaurus/plugin-content-docs" "^3.0.1" @@ -16376,24 +15633,24 @@ docusaurus-plugin-sass@^0.2.3: dependencies: sass-loader "^10.1.1" -docusaurus-theme-openapi-docs@3.0.0-beta.10: - version "3.0.0-beta.10" - resolved "https://registry.yarnpkg.com/docusaurus-theme-openapi-docs/-/docusaurus-theme-openapi-docs-3.0.0-beta.10.tgz#f9a790b1ef88ff01f266224064f4b2f80d0892ff" - integrity sha512-8oUMMZSrRJ9EssrjWwbM9aYuHOt1AAm6wQDzWr8k6VvefGvVAibg4Y9PK7GeZ243lJikq9s45KqUA0SMwsm+Fg== +docusaurus-theme-openapi-docs@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/docusaurus-theme-openapi-docs/-/docusaurus-theme-openapi-docs-4.0.1.tgz#fb10e9e253afa7d1012f47e73e9a89ab0e8121a5" + integrity sha512-4HIzYm2Y+pPiqvFs2oSEghtSgamza3Az1nGgwAJ+dpowfdOUafsGnbWOkJoFWVncRNn8/2mYSwrbUuo1t0kVUQ== dependencies: "@docusaurus/theme-common" "^3.0.1" "@hookform/error-message" "^2.0.1" - "@paloaltonetworks/postman-code-generators" "1.1.15-patch.2" - "@paloaltonetworks/postman-collection" "^4.1.0" "@reduxjs/toolkit" "^1.7.1" clsx "^1.1.1" copy-text-to-clipboard "^3.1.0" crypto-js "^4.1.1" - docusaurus-plugin-openapi-docs "^3.0.0-beta.10" + docusaurus-plugin-openapi-docs "^4.0.1" docusaurus-plugin-sass "^0.2.3" file-saver "^2.0.5" lodash "^4.17.20" node-polyfill-webpack-plugin "^2.0.1" + postman-code-generators "^1.10.1" + postman-collection "^4.4.0" prism-react-renderer "^2.3.0" react-hook-form "^7.43.8" react-live "^4.0.0" @@ -16495,7 +15752,7 @@ domutils@^2.5.2, domutils@^2.8.0: domelementtype "^2.2.0" domhandler "^4.2.0" -domutils@^3.0.1, domutils@^3.1.0: +domutils@^3.0.1: version "3.1.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e" integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA== @@ -16568,41 +15825,6 @@ dotenv@^8.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== -download@^6.2.2: - version "6.2.5" - resolved "https://registry.yarnpkg.com/download/-/download-6.2.5.tgz#acd6a542e4cd0bb42ca70cfc98c9e43b07039714" - integrity sha512-DpO9K1sXAST8Cpzb7kmEhogJxymyVUd5qz/vCOSyvwtp2Klj2XcDt5YUuasgxka44SxF0q5RriKIwJmQHG2AuA== - dependencies: - caw "^2.0.0" - content-disposition "^0.5.2" - decompress "^4.0.0" - ext-name "^5.0.0" - file-type "5.2.0" - filenamify "^2.0.0" - get-stream "^3.0.0" - got "^7.0.0" - make-dir "^1.0.0" - p-event "^1.0.0" - pify "^3.0.0" - -download@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/download/-/download-7.1.0.tgz#9059aa9d70b503ee76a132897be6dec8e5587233" - integrity sha512-xqnBTVd/E+GxJVrX5/eUJiLYjCGPwMpdL+jGhGU57BvtcA7wwhtHVbXBeUk51kOpW3S7Jn3BQbN9Q1R1Km2qDQ== - dependencies: - archive-type "^4.0.0" - caw "^2.0.1" - content-disposition "^0.5.2" - decompress "^4.2.0" - ext-name "^5.0.0" - file-type "^8.1.0" - filenamify "^2.0.0" - get-stream "^3.0.0" - got "^8.3.1" - make-dir "^1.2.0" - p-event "^2.1.0" - pify "^3.0.0" - dset@^3.1.1, dset@^3.1.2: version "3.1.4" resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.4.tgz#f8eaf5f023f068a036d08cd07dc9ffb7d0065248" @@ -16718,11 +15940,6 @@ elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4, elliptic@^6. minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -emittery@^0.10.2: - version "0.10.2" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" - integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw== - emittery@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" @@ -16783,14 +16000,6 @@ encodeurl@~2.0.0: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== -encoding-sniffer@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz#799569d66d443babe82af18c9f403498365ef1d5" - integrity sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg== - dependencies: - iconv-lite "^0.6.3" - whatwg-encoding "^3.1.1" - encoding@^0.1.12: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" @@ -16821,16 +16030,16 @@ endent@^2.0.1: fast-json-parse "^1.0.3" objectorarray "^1.0.5" -engine.io-client@~6.5.2: - version "6.5.4" - resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.5.4.tgz#b8bc71ed3f25d0d51d587729262486b4b33bd0d0" - integrity sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ== +engine.io-client@~6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.6.1.tgz#28a9cc4e90d448e1d0ba9369ad08a7af82f9956a" + integrity sha512-aYuoak7I+R83M/BBPIOs2to51BmFIpC1wZe6zZzMrT2llVsHy5cvcmdsJgP2Qz6smHu+sD9oexiSUAVd8OfBPw== dependencies: "@socket.io/component-emitter" "~3.1.0" debug "~4.3.1" engine.io-parser "~5.2.1" ws "~8.17.1" - xmlhttprequest-ssl "~2.0.0" + xmlhttprequest-ssl "~2.1.1" engine.io-parser@~5.2.1: version "5.2.3" @@ -16892,7 +16101,7 @@ env-paths@^3.0.0: resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-3.0.0.tgz#2f1e89c2f6dbd3408e1b1711dd82d62e317f58da" integrity sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A== -envinfo@^7.7.3, envinfo@^7.7.4: +envinfo@^7.7.4: version "7.14.0" resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.14.0.tgz#26dac5db54418f2a4c1159153a0b2ae980838aae" integrity sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg== @@ -17459,7 +16668,7 @@ escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== @@ -17790,7 +16999,7 @@ eslint-scope@^7.2.2: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-utils@^1.3.1, eslint-utils@^1.4.3: +eslint-utils@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== @@ -17826,48 +17035,6 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@^5.6.0: - version "5.16.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" - integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== - dependencies: - "@babel/code-frame" "^7.0.0" - ajv "^6.9.1" - chalk "^2.1.0" - cross-spawn "^6.0.5" - debug "^4.0.1" - doctrine "^3.0.0" - eslint-scope "^4.0.3" - eslint-utils "^1.3.1" - eslint-visitor-keys "^1.0.0" - espree "^5.0.1" - esquery "^1.0.1" - esutils "^2.0.2" - file-entry-cache "^5.0.1" - functional-red-black-tree "^1.0.1" - glob "^7.1.2" - globals "^11.7.0" - ignore "^4.0.6" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - inquirer "^6.2.2" - js-yaml "^3.13.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.11" - minimatch "^3.0.4" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - optionator "^0.8.2" - path-is-inside "^1.0.2" - progress "^2.0.0" - regexpp "^2.0.1" - semver "^5.5.1" - strip-ansi "^4.0.0" - strip-json-comments "^2.0.1" - table "^5.2.3" - text-table "^0.2.0" - eslint@^6.1.0: version "6.8.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" @@ -17965,15 +17132,6 @@ esniff@^2.0.1: event-emitter "^0.3.5" type "^2.7.2" -espree@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" - integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== - dependencies: - acorn "^6.0.7" - acorn-jsx "^5.0.0" - eslint-visitor-keys "^1.0.0" - espree@^6.1.2: version "6.2.1" resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" @@ -18421,17 +17579,6 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" -exec-buffer@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/exec-buffer/-/exec-buffer-3.2.0.tgz#b1686dbd904c7cf982e652c1f5a79b1e5573082b" - integrity sha512-wsiD+2Tp6BWHoVv3B+5Dcx6E7u5zky+hUwOHjuH2hKSLR3dvRmX8fk8UD8uqQixHs4Wk6eDmiegVrMPjKj7wpA== - dependencies: - execa "^0.7.0" - p-finally "^1.0.0" - pify "^3.0.0" - rimraf "^2.5.4" - tempfile "^2.0.0" - exec-sh@^0.3.2: version "0.3.6" resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" @@ -18468,19 +17615,6 @@ execa@5.1.1, execa@^5.0.0, execa@^5.1.1: signal-exit "^3.0.3" strip-final-newline "^2.0.0" -execa@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" - integrity sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw== - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - execa@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" @@ -18540,13 +17674,6 @@ execa@^8.0.1: signal-exit "^4.1.0" strip-final-newline "^3.0.0" -executable@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" - integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== - dependencies: - pify "^2.2.0" - exenv@^1.2.0: version "1.2.2" resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" @@ -18599,17 +17726,6 @@ expect@^25.5.0: jest-message-util "^25.5.0" jest-regex-util "^25.2.6" -expect@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/expect/-/expect-28.1.3.tgz#90a7c1a124f1824133dd4533cce2d2bdcb6603ec" - integrity sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g== - dependencies: - "@jest/expect-utils" "^28.1.3" - jest-get-type "^28.0.2" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - expect@^29.0.0, expect@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" @@ -18666,21 +17782,6 @@ express@^4.14.0, express@^4.17.1, express@^4.17.3, express@^4.18.2, express@^4.2 utils-merge "1.0.1" vary "~1.1.2" -ext-list@^2.0.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37" - integrity sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA== - dependencies: - mime-db "^1.28.0" - -ext-name@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ext-name/-/ext-name-5.0.0.tgz#70781981d183ee15d13993c8822045c506c8f0a6" - integrity sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ== - dependencies: - ext-list "^2.0.0" - sort-keys-length "^1.0.0" - ext@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" @@ -18853,18 +17954,6 @@ fast-url-parser@1.1.3, fast-url-parser@^1.1.3: dependencies: punycode "^1.3.2" -fast-xml-parser@^4.1.3: - version "4.5.0" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz#2882b7d01a6825dfdf909638f2de0256351def37" - integrity sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg== - dependencies: - strnum "^1.0.5" - -fastest-levenshtein@^1.0.12: - version "1.0.16" - resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" - integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== - fastq@^1.6.0: version "1.17.1" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" @@ -18925,6 +18014,11 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" +fdir@^6.1.1: + version "6.3.0" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.3.0.tgz#fcca5a23ea20e767b15e081ee13b3e6488ee0bb0" + integrity sha512-QOnuT+BOtivR77wYvCWHfGt9s4Pz1VIMbD463vegT5MLqNXy8rYFT/lPVEqf/bhYeT6qmqrNHhsX+rWwe3rOCQ== + feed@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/feed/-/feed-4.2.2.tgz#865783ef6ed12579e2c44bbef3c9113bc4956a7e" @@ -18942,21 +18036,6 @@ figgy-pudding@^3.5.1: resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== -figures@^1.3.5: - version "1.7.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" - integrity sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ== - dependencies: - escape-string-regexp "^1.0.5" - object-assign "^4.1.0" - -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" - integrity sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA== - dependencies: - escape-string-regexp "^1.0.5" - figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -18999,40 +18078,11 @@ file-system-cache@^1.0.5: fs-extra "^10.1.0" ramda "^0.28.0" -file-type@3.9.0, file-type@^3.8.0: +file-type@3.9.0: version "3.9.0" resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" integrity sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA== -file-type@5.2.0, file-type@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6" - integrity sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ== - -file-type@^16.5.3: - version "16.5.4" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-16.5.4.tgz#474fb4f704bee427681f98dd390058a172a6c2fd" - integrity sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw== - dependencies: - readable-web-to-node-stream "^3.0.0" - strtok3 "^6.2.4" - token-types "^4.1.1" - -file-type@^4.2.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-4.4.0.tgz#1b600e5fca1fbdc6e80c0a70c71c8dba5f7906c5" - integrity sha512-f2UbFQEk7LXgWpi5ntcO86OeA/cC80fuDDDaX/fZ2ZGel+AF7leRQqBBW1eJNiiQkrZlAoM6P+VYP5P6bOlDEQ== - -file-type@^6.1.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919" - integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg== - -file-type@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-8.1.0.tgz#244f3b7ef641bbe0cca196c7276e4b332399f68c" - integrity sha512-qyQ0pzAy78gVoJsmYeNgl8uH8yKhr1lVhW7JbzJmnlRi0I4R2eEDEJZVKG8agpDnLpacwNbDhLNG/LMdxHD2YQ== - file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -19045,20 +18095,6 @@ filelist@^1.0.4: 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" - integrity sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ== - -filenamify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-2.1.0.tgz#88faf495fb1b47abfd612300002a16228c677ee9" - integrity sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA== - dependencies: - filename-reserved-regex "^2.0.0" - strip-outer "^1.0.0" - trim-repeated "^1.0.0" - filesize@^8.0.6: version "8.0.7" resolved "https://registry.yarnpkg.com/filesize/-/filesize-8.0.7.tgz#695e70d80f4e47012c132d57a059e80c6b580bd8" @@ -19113,7 +18149,7 @@ find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: make-dir "^2.0.0" pkg-dir "^3.0.0" -find-cache-dir@^3.2.0, find-cache-dir@^3.3.1: +find-cache-dir@^3.2.0, find-cache-dir@^3.3.1, find-cache-dir@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== @@ -19188,13 +18224,6 @@ find-up@^6.3.0: locate-path "^7.1.0" path-exists "^5.0.0" -find-versions@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-3.2.0.tgz#10297f98030a786829681690545ef659ed1d254e" - integrity sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww== - dependencies: - semver-regex "^2.0.0" - find-yarn-workspace-root2@1.2.16: version "1.2.16" resolved "https://registry.yarnpkg.com/find-yarn-workspace-root2/-/find-yarn-workspace-root2-1.2.16.tgz#60287009dd2f324f59646bdb4b7610a6b301c2a9" @@ -19383,6 +18412,15 @@ formidable@^2.1.2: once "^1.4.0" qs "^6.11.0" +formidable@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-3.5.1.tgz#9360a23a656f261207868b1484624c4c8d06ee1a" + integrity sha512-WJWKelbRHN41m5dumb0/k8TeAx7Id/y3a+Z7QfhxP/htI9Js5zYaEDtG8uMgG0vM0lOlqnmjE99/kfpOYi/0Og== + dependencies: + dezalgo "^1.0.4" + hexoid "^1.0.0" + once "^1.4.0" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -19415,7 +18453,7 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== -from2@^2.1.0, from2@^2.1.1: +from2@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" integrity sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g== @@ -19467,7 +18505,7 @@ fs-extra@^0.30.0: path-is-absolute "^1.0.0" rimraf "^2.2.8" -fs-extra@^10.1.0: +fs-extra@^10.0.0, fs-extra@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== @@ -19702,13 +18740,6 @@ get-port@^5.1.1: resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== -get-proxy@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/get-proxy/-/get-proxy-2.1.0.tgz#349f2b4d91d44c4d4d4e9cba2ad90143fac5ef93" - integrity sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw== - dependencies: - npm-conf "^1.1.0" - get-source@^2.0.12: version "2.0.12" resolved "https://registry.yarnpkg.com/get-source/-/get-source-2.0.12.tgz#0b47d57ea1e53ce0d3a69f4f3d277eb8047da944" @@ -19727,19 +18758,6 @@ get-stdin@^6.0.0: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== -get-stream@3.0.0, get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - integrity sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ== - -get-stream@^2.2.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de" - integrity sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA== - dependencies: - object-assign "^4.0.1" - pinkie-promise "^2.0.0" - get-stream@^4.0.0, get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -20031,7 +19049,7 @@ global@^4.4.0, global@~4.4.0: min-document "^2.19.0" process "^0.11.10" -globals@^11.1.0, globals@^11.7.0: +globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== @@ -20101,18 +19119,6 @@ globby@^10.0.1: merge2 "^1.2.3" slash "^3.0.0" -globby@^12.0.0: - version "12.2.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-12.2.0.tgz#2ab8046b4fba4ff6eede835b29f678f90e3d3c22" - integrity sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA== - dependencies: - array-union "^3.0.1" - dir-glob "^3.0.1" - fast-glob "^3.2.7" - ignore "^5.1.9" - merge2 "^1.4.1" - slash "^4.0.0" - globby@^13.1.1: version "13.2.2" resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" @@ -20225,55 +19231,12 @@ got@^12.1.0: p-cancelable "^3.0.0" responselike "^3.0.0" -got@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" - integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== - dependencies: - decompress-response "^3.2.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-plain-obj "^1.1.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - p-cancelable "^0.3.0" - p-timeout "^1.1.1" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - url-parse-lax "^1.0.0" - url-to-options "^1.0.1" - -got@^8.3.1: - version "8.3.2" - resolved "https://registry.yarnpkg.com/got/-/got-8.3.2.tgz#1d23f64390e97f776cac52e5b936e5f514d2e937" - integrity sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw== - dependencies: - "@sindresorhus/is" "^0.7.0" - cacheable-request "^2.1.1" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - into-stream "^3.1.0" - is-retry-allowed "^1.1.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - mimic-response "^1.0.0" - p-cancelable "^0.4.0" - p-timeout "^2.0.1" - pify "^3.0.0" - safe-buffer "^5.1.1" - timed-out "^4.0.1" - url-parse-lax "^3.0.0" - url-to-options "^1.0.1" - graceful-fs@4.2.10: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== -graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.5, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.8, graceful-fs@^4.2.9: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.5, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -20486,13 +19449,6 @@ hardhat@2.22.2: uuid "^8.3.2" ws "^7.4.6" -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg== - dependencies: - ansi-regex "^2.0.0" - has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" @@ -20532,23 +19488,11 @@ has-proto@^1.0.1, has-proto@^1.0.3: resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== -has-symbol-support-x@^1.4.1: - version "1.4.2" - resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" - integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== - has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== -has-to-string-tag-x@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" - integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== - dependencies: - has-symbol-support-x "^1.4.1" - has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" @@ -21104,20 +20048,15 @@ htmlparser2@^7.2.0: domutils "^2.8.0" entities "^3.0.1" -htmlparser2@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-9.1.0.tgz#cdb498d8a75a51f739b61d3f718136c369bc8c23" - integrity sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ== +htmlparser2@^8.0.1: + version "8.0.2" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.2.tgz#f002151705b383e62433b5cf466f5b716edaec21" + integrity sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA== dependencies: domelementtype "^2.3.0" domhandler "^5.0.3" - domutils "^3.1.0" - entities "^4.5.0" - -http-cache-semantics@3.8.1: - version "3.8.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" - integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== + domutils "^3.0.1" + entities "^4.4.0" http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: version "4.1.1" @@ -21345,7 +20284,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.6.3, iconv-lite@^0.6.2, iconv-lite@^0.6.3: +iconv-lite@0.6.3, iconv-lite@^0.6.2: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -21413,14 +20352,6 @@ ignore@^5.1.1, ignore@^5.1.9, ignore@^5.2.0, ignore@^5.2.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== -image-minimizer-webpack-plugin@^3.8.3: - version "3.8.3" - resolved "https://registry.yarnpkg.com/image-minimizer-webpack-plugin/-/image-minimizer-webpack-plugin-3.8.3.tgz#41b2379f9d8adabf4e4db63c656543fee26dd4c2" - integrity sha512-Ex0cjNJc2FUSuwN7WHNyxkIZINP0M9lrN+uWJznMcsehiM5Z7ELwk+SEkSGEookK1GUd2wf+09jy1PEH5a5XmQ== - dependencies: - schema-utils "^4.2.0" - serialize-javascript "^6.0.1" - image-size@^1.0.2: version "1.1.1" resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.1.1.tgz#ddd67d4dc340e52ac29ce5f546a09f4e29e840ac" @@ -21428,45 +20359,6 @@ image-size@^1.0.2: dependencies: queue "6.0.2" -imagemin-jpegtran@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/imagemin-jpegtran/-/imagemin-jpegtran-7.0.0.tgz#7728f84876362d489b9a1656e0cc8e2009406e6f" - integrity sha512-MJoyTCW8YjMJf56NorFE41SR/WkaGA3IYk4JgvMlRwguJEEd3PnP9UxA8Y2UWjquz8d+On3Ds/03ZfiiLS8xTQ== - dependencies: - exec-buffer "^3.0.0" - is-jpg "^2.0.0" - jpegtran-bin "^5.0.0" - -imagemin-optipng@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/imagemin-optipng/-/imagemin-optipng-8.0.0.tgz#b88e5cf6da25cc8479e07cdf38c3ae0479df7ef2" - integrity sha512-CUGfhfwqlPjAC0rm8Fy+R2DJDBGjzy2SkfyT09L8rasnF9jSoHFqJ1xxSZWK6HVPZBMhGPMxCTL70OgTHlLF5A== - dependencies: - exec-buffer "^3.0.0" - is-png "^2.0.0" - optipng-bin "^7.0.0" - -imagemin-svgo@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/imagemin-svgo/-/imagemin-svgo-10.0.1.tgz#bc592950831c13998a40cb248f6e82e0b0b5c3dd" - integrity sha512-v27/UTGkb3vrm5jvjsMGQ2oxaDfSOTBfJOgmFO2fYepx05bY1IqWCK13aDytVR+l9w9eOlq0NMCLbxJlghYb2g== - dependencies: - is-svg "^4.3.1" - svgo "^2.5.0" - -imagemin@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/imagemin/-/imagemin-8.0.1.tgz#8b29ecb78197d8f0eac6a782f2e6b38fb3780d9e" - integrity sha512-Q/QaPi+5HuwbZNtQRqUVk6hKacI6z9iWiCSQBisAv7uBynZwO7t1svkryKl7+iSQbkU/6t9DWnHz04cFs2WY7w== - dependencies: - file-type "^16.5.3" - globby "^12.0.0" - graceful-fs "^4.2.8" - junk "^3.1.0" - p-pipe "^4.0.0" - replace-ext "^2.0.0" - slash "^3.0.0" - immer@^10.0.3: version "10.1.1" resolved "https://registry.yarnpkg.com/immer/-/immer-10.1.1.tgz#206f344ea372d8ea176891545ee53ccc062db7bc" @@ -21494,14 +20386,6 @@ import-cwd@^3.0.0: dependencies: import-from "^3.0.0" -import-fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" - integrity sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg== - dependencies: - caller-path "^2.0.0" - resolve-from "^3.0.0" - import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -21522,11 +20406,6 @@ import-from@^3.0.0: dependencies: resolve-from "^5.0.0" -import-lazy@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-3.1.0.tgz#891279202c8a2280fdbd6674dbd8da1a1dfc67cc" - integrity sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ== - import-lazy@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" @@ -21567,10 +20446,10 @@ infer-owner@^1.0.3, infer-owner@^1.0.4: resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== -infima@0.2.0-alpha.43: - version "0.2.0-alpha.43" - resolved "https://registry.yarnpkg.com/infima/-/infima-0.2.0-alpha.43.tgz#f7aa1d7b30b6c08afef441c726bac6150228cbe0" - integrity sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ== +infima@0.2.0-alpha.44: + version "0.2.0-alpha.44" + resolved "https://registry.yarnpkg.com/infima/-/infima-0.2.0-alpha.44.tgz#9cd9446e473b44d49763f48efabe31f32440861d" + integrity sha512-tuRkUSO/lB3rEhLJk25atwAjgLuzq070+pOW8XcvpHky/YbENnRRdPd85IBkyeTgttmOy5ah+yHYsK1HhUd4lQ== inflight@^1.0.4: version "1.0.6" @@ -21659,25 +20538,6 @@ inline-style-parser@0.2.4: resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.2.4.tgz#f4af5fe72e612839fcd453d989a586566d695f22" integrity sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q== -inquirer@^6.2.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" - integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== - dependencies: - ansi-escapes "^3.2.0" - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^3.0.3" - figures "^2.0.0" - lodash "^4.17.12" - mute-stream "0.0.7" - run-async "^2.2.0" - rxjs "^6.4.0" - string-width "^2.1.0" - strip-ansi "^5.1.0" - through "^2.3.6" - inquirer@^7.0.0, inquirer@^7.3.3: version "7.3.3" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" @@ -21742,11 +20602,6 @@ interpret@^2.2.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== -interpret@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" - integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== - intl-messageformat@^10.5.14: version "10.5.14" resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-10.5.14.tgz#e5bb373f8a37b88fbe647d7b941f3ab2a37ed00a" @@ -21757,14 +20612,6 @@ intl-messageformat@^10.5.14: "@formatjs/icu-messageformat-parser" "2.7.8" tslib "^2.4.0" -into-stream@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6" - integrity sha512-TcdjPibTksa1NQximqep2r17ISRiNE9fwlfbg3F8ANdvP5/yrFTew86VcO//jk4QTaMlbjypPBq76HN2zaKfZQ== - dependencies: - from2 "^2.1.1" - p-is-promise "^1.1.0" - invariant@2.2.4, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -21930,7 +20777,7 @@ is-buffer@^2.0.0: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== -is-builtin-module@^3.1.0, is-builtin-module@^3.2.1: +is-builtin-module@^3.1.0: version "3.2.1" resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== @@ -22017,11 +20864,6 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-accessor-descriptor "^1.0.1" is-data-descriptor "^1.0.1" -is-directory@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" - integrity sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw== - is-docker@^2.0.0, is-docker@^2.1.1: version "2.2.1" resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" @@ -22157,11 +20999,6 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== -is-jpg@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-jpg/-/is-jpg-2.0.0.tgz#2e1997fa6e9166eaac0242daae443403e4ef1d97" - integrity sha512-ODlO0ruzhkzD3sdynIainVP5eoOFNN85rxA1+cwwnPe4dKyX0r5+hxNO5XpCrxlHcmb9vkOit9mhRD2JVuimHg== - is-lambda@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" @@ -22192,11 +21029,6 @@ is-nan@^1.3.2: call-bind "^1.0.0" define-properties "^1.1.3" -is-natural-number@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8" - integrity sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ== - is-negative-zero@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" @@ -22283,11 +21115,6 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" -is-png@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-png/-/is-png-2.0.0.tgz#ee8cbc9e9b050425cedeeb4a6fb74a649b0a4a8d" - integrity sha512-4KPGizaVGj2LK7xwJIz8o5B2ubu1D/vcQsgOGFEDlpcvgZHto4gBnyd0ig7Ws+67ixmwKoNmu0hYnpo6AaKb5g== - is-reference@1.2.1, is-reference@^1.1.2: version "1.2.1" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" @@ -22322,11 +21149,6 @@ is-relative@^1.0.0: dependencies: is-unc-path "^1.0.0" -is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" - integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== - is-root@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" @@ -22351,7 +21173,7 @@ is-ssh@^1.3.0: dependencies: protocols "^2.0.1" -is-stream@^1.0.0, is-stream@^1.1.0: +is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== @@ -22380,13 +21202,6 @@ is-subdir@^1.1.1: dependencies: better-path-resolve "1.0.0" -is-svg@^4.3.1: - version "4.4.0" - resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-4.4.0.tgz#34db20a38146be5f2b3060154da33d11e6f74b7c" - integrity sha512-v+AgVwiK5DsGtT9ng+m4mClp6zDAmwrW8nZi6Gg15qzvBnRWWdfWA1TGaXyCDnWq5g5asofIgMVl3PjKxvk1ug== - dependencies: - fast-xml-parser "^4.1.3" - is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" @@ -22595,7 +21410,7 @@ istanbul-lib-instrument@^4.0.0: istanbul-lib-coverage "^3.0.0" semver "^6.3.0" -istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: +istanbul-lib-instrument@^5.0.4: version "5.2.1" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== @@ -22655,14 +21470,6 @@ istanbul-reports@^3.0.2, istanbul-reports@^3.1.3, istanbul-reports@^3.1.4: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -isurl@^1.0.0-alpha5: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" - integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== - dependencies: - has-to-string-tag-x "^1.2.0" - is-object "^1.0.1" - iterate-iterator@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/iterate-iterator/-/iterate-iterator-1.0.2.tgz#551b804c9eaa15b847ea6a7cdc2f5bf1ec150f91" @@ -22715,14 +21522,6 @@ jest-changed-files@^25.5.0: execa "^3.2.0" throat "^5.0.0" -jest-changed-files@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-28.1.3.tgz#d9aeee6792be3686c47cb988a8eaf82ff4238831" - integrity sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA== - dependencies: - execa "^5.0.0" - p-limit "^3.1.0" - jest-changed-files@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" @@ -22732,31 +21531,6 @@ jest-changed-files@^29.7.0: jest-util "^29.7.0" p-limit "^3.1.0" -jest-circus@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-28.1.3.tgz#d14bd11cf8ee1a03d69902dc47b6bd4634ee00e4" - integrity sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow== - dependencies: - "@jest/environment" "^28.1.3" - "@jest/expect" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^0.7.0" - is-generator-fn "^2.0.0" - jest-each "^28.1.3" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-runtime "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" - p-limit "^3.1.0" - pretty-format "^28.1.3" - slash "^3.0.0" - stack-utils "^2.0.3" - jest-circus@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" @@ -22803,24 +21577,6 @@ jest-cli@^25.5.4: realpath-native "^2.0.0" yargs "^15.3.1" -jest-cli@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-28.1.3.tgz#558b33c577d06de55087b8448d373b9f654e46b2" - integrity sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ== - dependencies: - "@jest/core" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - import-local "^3.0.2" - jest-config "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" - prompts "^2.0.1" - yargs "^17.3.1" - jest-cli@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" @@ -22863,34 +21619,6 @@ jest-config@^25.5.4: pretty-format "^25.5.0" realpath-native "^2.0.0" -jest-config@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-28.1.3.tgz#e315e1f73df3cac31447eed8b8740a477392ec60" - integrity sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ== - dependencies: - "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^28.1.3" - "@jest/types" "^28.1.3" - babel-jest "^28.1.3" - chalk "^4.0.0" - ci-info "^3.2.0" - deepmerge "^4.2.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-circus "^28.1.3" - jest-environment-node "^28.1.3" - jest-get-type "^28.0.2" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-runner "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" - micromatch "^4.0.4" - parse-json "^5.2.0" - pretty-format "^28.1.3" - slash "^3.0.0" - strip-json-comments "^3.1.1" - jest-config@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" @@ -22939,16 +21667,6 @@ jest-diff@^25.2.1, jest-diff@^25.5.0: jest-get-type "^25.2.6" pretty-format "^25.5.0" -jest-diff@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.3.tgz#948a192d86f4e7a64c5264ad4da4877133d8792f" - integrity sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw== - dependencies: - chalk "^4.0.0" - diff-sequences "^28.1.1" - jest-get-type "^28.0.2" - pretty-format "^28.1.3" - jest-diff@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" @@ -22966,13 +21684,6 @@ jest-docblock@^25.3.0: dependencies: detect-newline "^3.0.0" -jest-docblock@^28.1.1: - version "28.1.1" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-28.1.1.tgz#6f515c3bf841516d82ecd57a62eed9204c2f42a8" - integrity sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA== - dependencies: - detect-newline "^3.0.0" - jest-docblock@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" @@ -22991,17 +21702,6 @@ jest-each@^25.5.0: jest-util "^25.5.0" pretty-format "^25.5.0" -jest-each@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-28.1.3.tgz#bdd1516edbe2b1f3569cfdad9acd543040028f81" - integrity sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g== - dependencies: - "@jest/types" "^28.1.3" - chalk "^4.0.0" - jest-get-type "^28.0.2" - jest-util "^28.1.3" - pretty-format "^28.1.3" - jest-each@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" @@ -23037,18 +21737,6 @@ jest-environment-node@^25.5.0: jest-util "^25.5.0" semver "^6.3.0" -jest-environment-node@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-28.1.3.tgz#7e74fe40eb645b9d56c0c4b70ca4357faa349be5" - integrity sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A== - dependencies: - "@jest/environment" "^28.1.3" - "@jest/fake-timers" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - jest-mock "^28.1.3" - jest-util "^28.1.3" - jest-environment-node@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" @@ -23079,11 +21767,6 @@ jest-get-type@^25.2.6: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877" integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig== -jest-get-type@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203" - integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA== - jest-get-type@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" @@ -23130,25 +21813,6 @@ jest-haste-map@^26.6.2: optionalDependencies: fsevents "^2.1.2" -jest-haste-map@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-28.1.3.tgz#abd5451129a38d9841049644f34b034308944e2b" - integrity sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA== - dependencies: - "@jest/types" "^28.1.3" - "@types/graceful-fs" "^4.1.3" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.9" - jest-regex-util "^28.0.2" - jest-util "^28.1.3" - jest-worker "^28.1.3" - micromatch "^4.0.4" - walker "^1.0.8" - optionalDependencies: - fsevents "^2.3.2" - jest-haste-map@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" @@ -23199,14 +21863,6 @@ jest-leak-detector@^25.5.0: jest-get-type "^25.2.6" pretty-format "^25.5.0" -jest-leak-detector@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz#a6685d9b074be99e3adee816ce84fd30795e654d" - integrity sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA== - dependencies: - jest-get-type "^28.0.2" - pretty-format "^28.1.3" - jest-leak-detector@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" @@ -23225,16 +21881,6 @@ jest-matcher-utils@^25.5.0: jest-get-type "^25.2.6" pretty-format "^25.5.0" -jest-matcher-utils@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e" - integrity sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw== - dependencies: - chalk "^4.0.0" - jest-diff "^28.1.3" - jest-get-type "^28.0.2" - pretty-format "^28.1.3" - jest-matcher-utils@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" @@ -23259,21 +21905,6 @@ jest-message-util@^25.5.0: slash "^3.0.0" stack-utils "^1.0.1" -jest-message-util@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d" - integrity sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^28.1.3" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^28.1.3" - slash "^3.0.0" - stack-utils "^2.0.3" - jest-message-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" @@ -23311,14 +21942,6 @@ jest-mock@^27.0.6: "@jest/types" "^27.5.1" "@types/node" "*" -jest-mock@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-28.1.3.tgz#d4e9b1fc838bea595c77ab73672ebf513ab249da" - integrity sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA== - dependencies: - "@jest/types" "^28.1.3" - "@types/node" "*" - jest-mock@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" @@ -23343,11 +21966,6 @@ jest-regex-util@^26.0.0: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== -jest-regex-util@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-28.0.2.tgz#afdc377a3b25fb6e80825adcf76c854e5bf47ead" - integrity sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw== - jest-regex-util@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" @@ -23362,14 +21980,6 @@ jest-resolve-dependencies@^25.5.4: jest-regex-util "^25.2.6" jest-snapshot "^25.5.1" -jest-resolve-dependencies@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz#8c65d7583460df7275c6ea2791901fa975c1fe66" - integrity sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA== - dependencies: - jest-regex-util "^28.0.2" - jest-snapshot "^28.1.3" - jest-resolve-dependencies@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" @@ -23393,21 +22003,6 @@ jest-resolve@^25.5.1: resolve "^1.17.0" slash "^3.0.0" -jest-resolve@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-28.1.3.tgz#cfb36100341ddbb061ec781426b3c31eb51aa0a8" - integrity sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ== - dependencies: - chalk "^4.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - jest-pnp-resolver "^1.2.2" - jest-util "^28.1.3" - jest-validate "^28.1.3" - resolve "^1.20.0" - resolve.exports "^1.1.0" - slash "^3.0.0" - jest-resolve@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" @@ -23448,33 +22043,6 @@ jest-runner@^25.5.4: source-map-support "^0.5.6" throat "^5.0.0" -jest-runner@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-28.1.3.tgz#5eee25febd730b4713a2cdfd76bdd5557840f9a1" - integrity sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA== - dependencies: - "@jest/console" "^28.1.3" - "@jest/environment" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.10.2" - graceful-fs "^4.2.9" - jest-docblock "^28.1.1" - jest-environment-node "^28.1.3" - jest-haste-map "^28.1.3" - jest-leak-detector "^28.1.3" - jest-message-util "^28.1.3" - jest-resolve "^28.1.3" - jest-runtime "^28.1.3" - jest-util "^28.1.3" - jest-watcher "^28.1.3" - jest-worker "^28.1.3" - p-limit "^3.1.0" - source-map-support "0.5.13" - jest-runner@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" @@ -23534,34 +22102,6 @@ jest-runtime@^25.5.4: strip-bom "^4.0.0" yargs "^15.3.1" -jest-runtime@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-28.1.3.tgz#a57643458235aa53e8ec7821949e728960d0605f" - integrity sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw== - dependencies: - "@jest/environment" "^28.1.3" - "@jest/fake-timers" "^28.1.3" - "@jest/globals" "^28.1.3" - "@jest/source-map" "^28.1.2" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - execa "^5.0.0" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - jest-message-util "^28.1.3" - jest-mock "^28.1.3" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" - slash "^3.0.0" - strip-bom "^4.0.0" - jest-runtime@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" @@ -23626,35 +22166,6 @@ jest-snapshot@^25.5.1: pretty-format "^25.5.0" semver "^6.3.0" -jest-snapshot@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-28.1.3.tgz#17467b3ab8ddb81e2f605db05583d69388fc0668" - integrity sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg== - dependencies: - "@babel/core" "^7.11.6" - "@babel/generator" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" - "@babel/types" "^7.3.3" - "@jest/expect-utils" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/babel__traverse" "^7.0.6" - "@types/prettier" "^2.1.5" - babel-preset-current-node-syntax "^1.0.0" - chalk "^4.0.0" - expect "^28.1.3" - graceful-fs "^4.2.9" - jest-diff "^28.1.3" - jest-get-type "^28.0.2" - jest-haste-map "^28.1.3" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - natural-compare "^1.4.0" - pretty-format "^28.1.3" - semver "^7.3.5" - jest-snapshot@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" @@ -23704,18 +22215,6 @@ jest-util@^26.6.2: is-ci "^2.0.0" micromatch "^4.0.2" -jest-util@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0" - integrity sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ== - dependencies: - "@jest/types" "^28.1.3" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - jest-util@^29.0.0, jest-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" @@ -23740,18 +22239,6 @@ jest-validate@^25.5.0: leven "^3.1.0" pretty-format "^25.5.0" -jest-validate@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-28.1.3.tgz#e322267fd5e7c64cea4629612c357bbda96229df" - integrity sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA== - dependencies: - "@jest/types" "^28.1.3" - camelcase "^6.2.0" - chalk "^4.0.0" - jest-get-type "^28.0.2" - leven "^3.1.0" - pretty-format "^28.1.3" - jest-validate@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" @@ -23789,20 +22276,6 @@ jest-watcher@^25.2.4, jest-watcher@^25.5.0: jest-util "^25.5.0" string-length "^3.1.0" -jest-watcher@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-28.1.3.tgz#c6023a59ba2255e3b4c57179fc94164b3e73abd4" - integrity sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g== - dependencies: - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - emittery "^0.10.2" - jest-util "^28.1.3" - string-length "^4.0.1" - jest-watcher@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" @@ -23851,16 +22324,7 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-28.1.3.tgz#7e3c4ce3fa23d1bb6accb169e7f396f98ed4bb98" - integrity sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^8.0.0" - -jest-worker@^29.1.2, jest-worker@^29.4.3, jest-worker@^29.7.0: +jest-worker@^29.4.3, jest-worker@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== @@ -23879,16 +22343,6 @@ jest@^25.3.0: import-local "^3.0.2" jest-cli "^25.5.4" -jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest/-/jest-28.1.3.tgz#e9c6a7eecdebe3548ca2b18894a50f45b36dfc6b" - integrity sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA== - dependencies: - "@jest/core" "^28.1.3" - "@jest/types" "^28.1.3" - import-local "^3.0.2" - jest-cli "^28.1.3" - jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" @@ -23920,15 +22374,6 @@ jose@^5.0.0: resolved "https://registry.yarnpkg.com/jose/-/jose-5.9.2.tgz#22a22da06edb8fb9e583aa24bafc1e8457b4db92" integrity sha512-ILI2xx/I57b20sd7rHZvgiiQrmp2mcotwsAH+5ajbpFQbrYVQdNHYlQhoA5cFb78CgtBOxtC05TeA+mcgkuCqQ== -jpegtran-bin@^5.0.0: - version "5.0.2" - resolved "https://registry.yarnpkg.com/jpegtran-bin/-/jpegtran-bin-5.0.2.tgz#5870fd7e68317bd203a1c94572bd06ae7732cac3" - integrity sha512-4FSmgIcr8d5+V6T1+dHbPZjaFH0ogVyP4UVsE+zri7S9YLO4qAT2our4IN3sW3STVgNTbqPermdIgt2XuAJ4EA== - dependencies: - bin-build "^3.0.0" - bin-wrapper "^4.0.0" - logalot "^2.0.0" - jpjs@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/jpjs/-/jpjs-1.2.1.tgz#f343833de8838a5beba1f42d5a219be0114c44b7" @@ -23964,7 +22409,7 @@ js-string-escape@^1.0.1: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@3.14.1, js-yaml@3.x, js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.14.1, js-yaml@^3.6.1: +js-yaml@3.14.1, js-yaml@3.x, js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.14.1, js-yaml@^3.6.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -24239,13 +22684,6 @@ keccak@^3.0.0, keccak@^3.0.2, keccak@^3.0.3: node-gyp-build "^4.2.0" readable-stream "^3.6.0" -keyv@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" - integrity sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA== - dependencies: - json-buffer "3.0.0" - keyv@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" @@ -24756,7 +23194,7 @@ lodash.uniq@4.5.0, lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== -lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0, lodash@~4.17.0: +lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0, lodash@~4.17.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -24802,14 +23240,6 @@ log-update@^4.0.0: slice-ansi "^4.0.0" wrap-ansi "^6.2.0" -logalot@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/logalot/-/logalot-2.1.0.tgz#5f8e8c90d304edf12530951a5554abb8c5e3f552" - integrity sha512-Ah4CgdSRfeCJagxQhcVNMi9BfGYyEKLa6d7OA6xSbld/Hg3Cf2QiOa1mDpmG7Ve8LOH6DN3mdttzjQAvWTyVkw== - dependencies: - figures "^1.3.5" - squeak "^1.0.0" - logrocket-react@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/logrocket-react/-/logrocket-react-6.0.3.tgz#62c2ce18b9197058f5ba60e54e3f2b084d6588ee" @@ -24837,11 +23267,6 @@ longest-streak@^3.0.0: resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4" integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== -longest@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" - integrity sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg== - loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -24878,11 +23303,6 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" -lowercase-keys@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" - integrity sha512-RPlX0+PHuvxVDZ7xX+EBVAp4RsVxP/TdDSN2mJYdiq1Lc4Hz7EUSjUI7RZrKKlmrIzVhf6Jo2stj7++gVarS0A== - lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" @@ -24898,16 +23318,6 @@ lowercase-keys@^3.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== -lpad-align@^1.0.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/lpad-align/-/lpad-align-1.1.2.tgz#21f600ac1c3095c3c6e497ee67271ee08481fe9e" - integrity sha512-MMIcFmmR9zlGZtBcFOows6c2COMekHCIFJz3ew/rRpKZ1wR4mXDPzvcVqLarux8M33X4TPSq2Jdw8WJj0q0KbQ== - dependencies: - get-stdin "^4.0.1" - indent-string "^2.1.0" - longest "^1.0.0" - meow "^3.3.0" - lru-cache@^10.0.0, lru-cache@^10.2.0, lru-cache@^10.4.3: version "10.4.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" @@ -24964,13 +23374,6 @@ magic-string@^0.30.10, magic-string@^0.30.11, magic-string@^0.30.3, magic-string dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" -make-dir@^1.0.0, make-dir@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" - integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== - dependencies: - pify "^3.0.0" - make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -25648,7 +24051,7 @@ memorystream@^0.3.1: resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== -meow@^3.1.0, meow@^3.3.0: +meow@^3.1.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" integrity sha512-TNdwZs0skRlpPpCUK25StC4VH+tP5GgeY1HQOOGP+lQ2xtdkN2VtT/5tiX9k3IWpkBPV9b3LsAWXn4GGi/PrSA== @@ -26438,17 +24841,12 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.51.0: - version "1.51.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" - integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== - mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -"mime-db@>= 1.43.0 < 2", mime-db@^1.28.0: +"mime-db@>= 1.43.0 < 2": version "1.53.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.53.0.tgz#3cb63cd820fc29896d9d4e8c32ab4fcd74ccb447" integrity sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg== @@ -26472,13 +24870,6 @@ mime-types@2.1.18: dependencies: mime-db "~1.33.0" -mime-types@2.1.34: - version "2.1.34" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" - integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== - dependencies: - mime-db "1.51.0" - mime-types@2.1.35, mime-types@^2.1.12, mime-types@^2.1.16, mime-types@^2.1.27, mime-types@^2.1.30, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" @@ -27021,11 +25412,6 @@ mustache@^4.2.0: resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== -mute-stream@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" - integrity sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ== - mute-stream@0.0.8, mute-stream@~0.0.4: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" @@ -27518,15 +25904,6 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== -normalize-url@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" - integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw== - dependencies: - prepend-http "^2.0.0" - query-string "^5.0.1" - sort-keys "^2.0.0" - normalize-url@^4.1.0: version "4.5.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" @@ -27549,14 +25926,6 @@ npm-bundled@^1.1.1: dependencies: npm-normalize-package-bin "^1.0.1" -npm-conf@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9" - integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== - dependencies: - config-chain "^1.1.11" - pify "^3.0.0" - npm-install-checks@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-4.0.0.tgz#a37facc763a2fde0497ef2c6d0ac7c3fbe00d7b4" @@ -27773,18 +26142,6 @@ oas-linter@^3.2.2: should "^13.2.1" yaml "^1.10.0" -oas-resolver-browser@2.5.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/oas-resolver-browser/-/oas-resolver-browser-2.5.2.tgz#d972525a840d7a74ab1aa43e215e9531a99412ba" - integrity sha512-L3ugWyBHOpKLT+lb+pFXCOpk3byh6usis5T9u9mfu92jH5bR6YK8MA2bebUTIjY7I4415PzDeZcmcc+i7X05MA== - dependencies: - node-fetch-h2 "^2.3.0" - oas-kit-common "^1.0.8" - path-browserify "^1.0.1" - reftools "^1.1.6" - yaml "^1.10.0" - yargs "^15.3.1" - oas-resolver-browser@2.5.6: version "2.5.6" resolved "https://registry.yarnpkg.com/oas-resolver-browser/-/oas-resolver-browser-2.5.6.tgz#1974db66d594fa8c67d3aa866b46b9e2156a8b55" @@ -28105,7 +26462,7 @@ optimism@^0.18.0: "@wry/trie" "^0.4.3" tslib "^2.3.0" -optionator@^0.8.1, optionator@^0.8.2, optionator@^0.8.3: +optionator@^0.8.1, optionator@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== @@ -28129,14 +26486,6 @@ optionator@^0.9.3: type-check "^0.4.0" word-wrap "^1.2.5" -optipng-bin@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/optipng-bin/-/optipng-bin-7.0.1.tgz#beb8e55a52f8a26f885ee57ab44fcf62397d6972" - integrity sha512-W99mpdW7Nt2PpFiaO+74pkht7KEqkXkeRomdWXfEz3SALZ6hns81y/pm1dsGZ6ItUIfchiNIP6ORDr1zETU1jA== - dependencies: - bin-build "^3.0.0" - bin-wrapper "^4.0.0" - ora@^4.0.3: version "4.1.1" resolved "https://registry.yarnpkg.com/ora/-/ora-4.1.1.tgz#566cc0348a15c36f5f0e979612842e02ba9dddbc" @@ -28171,13 +26520,6 @@ os-browserify@^0.3.0: resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A== -os-filter-obj@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/os-filter-obj/-/os-filter-obj-2.0.0.tgz#1c0b62d5f3a2442749a2d139e6dddee6e81d8d16" - integrity sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg== - dependencies: - arch "^2.1.0" - os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" @@ -28213,16 +26555,6 @@ p-all@^2.1.0: dependencies: p-map "^2.0.0" -p-cancelable@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" - integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== - -p-cancelable@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.4.1.tgz#35f363d67d52081c8d9585e37bcceb7e0bbcb2a0" - integrity sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ== - p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" @@ -28248,20 +26580,6 @@ p-each-series@^2.1.0: resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" integrity sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA== -p-event@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-event/-/p-event-1.3.0.tgz#8e6b4f4f65c72bc5b6fe28b75eda874f96a4a085" - integrity sha512-hV1zbA7gwqPVFcapfeATaNjQ3J0NuzorHPyG8GPL9g/Y/TplWVBVoCKCXL6Ej2zscrCEv195QNWJXuBH6XZuzA== - dependencies: - p-timeout "^1.1.1" - -p-event@^2.1.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/p-event/-/p-event-2.3.1.tgz#596279ef169ab2c3e0cae88c1cfbb08079993ef6" - integrity sha512-NQCqOFhbpVTMX4qMe8PF8lbGtzZ+LCiN7pcNrb/413Na7+TRoe1xkKUzuWa/YEJdGQ0FvKtj35EEbDoVPO2kbA== - dependencies: - p-timeout "^2.0.1" - p-event@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5" @@ -28286,11 +26604,6 @@ p-finally@^2.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561" integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw== -p-is-promise@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" - integrity sha512-zL7VE4JVS2IFSkR2GQKDSPEVxkoH43/p7oEnwpdCndKYJO0HVeRB7fA8TJwuLOTBREtK0ea8eHaxdwcpob5dmg== - p-limit@3.1.0, p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" @@ -28354,13 +26667,6 @@ p-locate@^6.0.0: dependencies: p-limit "^4.0.0" -p-map-series@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-1.0.0.tgz#bf98fe575705658a9e1351befb85ae4c1f07bdca" - integrity sha512-4k9LlvY6Bo/1FcIdV33wqZQES0Py+iKISU9Uc8p8AjWoZPnFKMpVIVD3s0EYn4jzLh1I+WeUZkJ0Yoa4Qfw3Kg== - dependencies: - p-reduce "^1.0.0" - p-map-series@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2" @@ -28390,11 +26696,6 @@ p-pipe@^3.1.0: resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-3.1.0.tgz#48b57c922aa2e1af6a6404cb7c6bf0eb9cc8e60e" integrity sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw== -p-pipe@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-4.0.0.tgz#7e5424569351b2ab452a47826acb93ce09ad6a2c" - integrity sha512-HkPfFklpZQPUKBFXzKFB6ihLriIHxnmuQdK9WmLDwe4hf2PdhhfWT/FJa+pc3bA1ywvKXtedxIRmd4Y7BTXE4w== - p-queue@^6.6.2: version "6.6.2" resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" @@ -28411,11 +26712,6 @@ p-queue@^7.4.1: eventemitter3 "^5.0.1" p-timeout "^5.0.2" -p-reduce@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" - integrity sha512-3Tx1T3oM1xO/Y8Gj0sWyE78EIJZ+t+aEmXUdvQgvGmSMri7aPTHoovbXEreWKkL5j21Er60XAWLTzKbAKYOujQ== - p-reduce@^2.0.0, p-reduce@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-2.1.0.tgz#09408da49507c6c274faa31f28df334bc712b64a" @@ -28429,20 +26725,6 @@ p-retry@^4.5.0: "@types/retry" "0.12.0" retry "^0.13.1" -p-timeout@^1.1.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" - integrity sha512-gb0ryzr+K2qFqFv6qi3khoeqMZF/+ajxQipEF6NteZVnvz9tzdsfAVj3lYtn1gAXvH5lfLwfxEII799gt/mRIA== - dependencies: - p-finally "^1.0.0" - -p-timeout@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" - integrity sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA== - dependencies: - p-finally "^1.0.0" - p-timeout@^3.0.0, p-timeout@^3.1.0, p-timeout@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" @@ -28686,13 +26968,6 @@ parse5-htmlparser2-tree-adapter@^7.0.0: domhandler "^5.0.2" parse5 "^7.0.0" -parse5-parser-stream@^7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz#d7c20eadc37968d272e2c02660fff92dd27e60e1" - integrity sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow== - dependencies: - parse5 "^7.0.0" - parse5@5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" @@ -28703,7 +26978,7 @@ parse5@^6.0.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== -parse5@^7.0.0, parse5@^7.1.2: +parse5@^7.0.0: version "7.1.2" resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== @@ -28783,7 +27058,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-is-inside@1.0.2, path-is-inside@^1.0.2: +path-is-inside@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== @@ -28884,7 +27159,7 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -path@^0.12.7: +path@0.12.7, path@^0.12.7: version "0.12.7" resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" integrity sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q== @@ -28925,11 +27200,6 @@ pcre-to-regexp@^1.1.0: resolved "https://registry.yarnpkg.com/pcre-to-regexp/-/pcre-to-regexp-1.1.0.tgz#1c48373d194b982e1416031b41470839fab3ad6c" integrity sha512-KF9XxmUQJ2DIlMj3TqNqY1AWvyvTuIuq11CuuekxyaYMiFuMKGgQrePYMX5bXKLhLG3sDI4CsGAYHPaT7VV7+g== -peek-readable@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-4.1.0.tgz#4ece1111bf5c2ad8867c314c81356847e8a62e72" - integrity sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg== - pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -29043,7 +27313,7 @@ picomatch@^2.0.4, picomatch@^2.0.7, picomatch@^2.2.1, picomatch@^2.2.2, picomatc resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pify@^2.0.0, pify@^2.2.0, pify@^2.3.0: +pify@^2.0.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== @@ -29327,13 +27597,6 @@ postcss-discard-overridden@^6.0.2: resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz#4e9f9c62ecd2df46e8fdb44dc17e189776572e2d" integrity sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ== -postcss-discard-unused@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-5.1.0.tgz#8974e9b143d887677304e558c1166d3762501142" - integrity sha512-KwLWymI9hbwXmJa0dkrzpRbSJEh0vVUd7r8t0yOGPcfKzyJJxFM8kLyC5Ev9avji6nY95pOp1W6HqIrfT+0VGw== - dependencies: - postcss-selector-parser "^6.0.5" - postcss-discard-unused@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-6.0.5.tgz#c1b0e8c032c6054c3fbd22aaddba5b248136f338" @@ -29400,14 +27663,6 @@ postcss-loader@^7.3.3: jiti "^1.20.0" semver "^7.5.4" -postcss-merge-idents@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-5.1.1.tgz#7753817c2e0b75d0853b56f78a89771e15ca04a1" - integrity sha512-pCijL1TREiCoog5nQp7wUe+TUonA2tC2sQ54UGeMmryK3UFGIYKqDyjnqd6RcuI4znFn9hWSLNN8xKE/vWcUQw== - dependencies: - cssnano-utils "^3.1.0" - postcss-value-parser "^4.2.0" - postcss-merge-idents@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-6.0.3.tgz#7b9c31c7bc823c94bec50f297f04e3c2b838ea65" @@ -29739,13 +27994,6 @@ postcss-ordered-values@^6.0.2: cssnano-utils "^4.0.2" postcss-value-parser "^4.2.0" -postcss-reduce-idents@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-5.2.0.tgz#c89c11336c432ac4b28792f24778859a67dfba95" - integrity sha512-BTrLjICoSB6gxbc58D5mdBK8OhXRDqud/zodYfdSi52qvDHdMwk+9kB9xsM8yJThH/sZU5A6QVSmMmaN001gIg== - dependencies: - postcss-value-parser "^4.2.0" - postcss-reduce-idents@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-6.0.3.tgz#b0d9c84316d2a547714ebab523ec7d13704cd486" @@ -29799,13 +28047,6 @@ postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.11, postcss-selecto cssesc "^3.0.0" util-deprecate "^1.0.2" -postcss-sort-media-queries@^4.4.1: - version "4.4.1" - resolved "https://registry.yarnpkg.com/postcss-sort-media-queries/-/postcss-sort-media-queries-4.4.1.tgz#04a5a78db3921eb78f28a1a781a2e68e65258128" - integrity sha512-QDESFzDDGKgpiIh4GYXsSy6sek2yAwQx1JASl5AxBtU1Lq2JfKBljIPNdil989NcSKRQX1ToiaKphImtBuhXWw== - dependencies: - sort-css-media-queries "2.1.0" - postcss-sort-media-queries@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/postcss-sort-media-queries/-/postcss-sort-media-queries-5.2.0.tgz#4556b3f982ef27d3bac526b99b6c0d3359a6cf97" @@ -29848,11 +28089,6 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0, postcss-value-parser@^ resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss-zindex@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-5.1.0.tgz#4a5c7e5ff1050bd4c01d95b1847dfdcc58a496ff" - integrity sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A== - postcss-zindex@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-6.0.2.tgz#e498304b83a8b165755f53db40e2ea65a99b56e1" @@ -29875,7 +28111,7 @@ postcss@^7.0.14, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.36, postcss@^7.0 picocolors "^0.2.1" source-map "^0.6.1" -postcss@^8.2.15, postcss@^8.4.13, postcss@^8.4.17, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.24, postcss@^8.4.26, postcss@^8.4.31, postcss@^8.4.33, postcss@^8.4.38, postcss@^8.4.43, postcss@^8.4.47: +postcss@^8.2.15, postcss@^8.4.13, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.24, postcss@^8.4.26, postcss@^8.4.31, postcss@^8.4.33, postcss@^8.4.38, postcss@^8.4.43, postcss@^8.4.47: version "8.4.47" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365" integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ== @@ -29933,6 +28169,18 @@ postgres-range@^1.1.1: resolved "https://registry.yarnpkg.com/postgres-range/-/postgres-range-1.1.4.tgz#a59c5f9520909bcec5e63e8cf913a92e4c952863" integrity sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w== +postman-code-generators@^1.10.1: + version "1.13.0" + resolved "https://registry.yarnpkg.com/postman-code-generators/-/postman-code-generators-1.13.0.tgz#ac355caf18f96fac53ad1da08ac8a30c936dc55f" + integrity sha512-rbKtX+PWp+4McQpAncnRCUKqDiynt4fDB1I7AzrsvBAQc74ab6k6K3IP8qvf0icqiPuf9nYHCSdy/LB922dQLQ== + dependencies: + async "3.2.2" + detect-package-manager "3.0.2" + lodash "4.17.21" + path "0.12.7" + postman-collection "^4.4.0" + shelljs "0.8.5" + postman-collection@^4.4.0: version "4.5.0" resolved "https://registry.yarnpkg.com/postman-collection/-/postman-collection-4.5.0.tgz#cc485d67f2177d6f4c5c5f4bc75c257efd23f221" @@ -30000,11 +28248,6 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== -prepend-http@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" - integrity sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg== - prepend-http@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" @@ -30017,7 +28260,7 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier-plugin-solidity@^1.0.0-beta.13, prettier-plugin-solidity@^1.0.0-beta.19: +prettier-plugin-solidity@^1.0.0-beta.13: version "1.4.1" resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.4.1.tgz#8060baf18853a9e34d2e09e47e87b4f19e15afe9" integrity sha512-Mq8EtfacVZ/0+uDKTtHZGW3Aa7vEbX/BNx63hmVg6YTiTXSiuKP0amj0G6pGwjmLaOfymWh3QgXEZkjQbU8QRg== @@ -30030,12 +28273,12 @@ prettier-plugin-solidity@^1.0.0-beta.13, prettier-plugin-solidity@^1.0.0-beta.19 resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18" integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w== -prettier@^1.14.3, prettier@^1.19.1: +prettier@^1.19.1: version "1.19.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== -prettier@^2.3.1, prettier@^2.5.1, prettier@^2.7.1, prettier@^2.8.0, prettier@^2.8.3: +prettier@^2.3.1, prettier@^2.7.1, prettier@^2.8.0, prettier@^2.8.3: version "2.8.8" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== @@ -30085,16 +28328,6 @@ pretty-format@^27.0.2: ansi-styles "^5.0.0" react-is "^17.0.1" -pretty-format@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5" - integrity sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q== - dependencies: - "@jest/schemas" "^28.1.3" - ansi-regex "^5.0.1" - ansi-styles "^5.0.0" - react-is "^18.0.0" - pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" @@ -30662,7 +28895,7 @@ react-docgen@^5.0.0: node-dir "^0.1.10" strip-indent "^3.0.0" -react-dom@^18.0.0, react-dom@^18.2.0: +react-dom@^18.2.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== @@ -30980,7 +29213,7 @@ react-transition-group@^4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" -react@^18.0.0, react@^18.2.0: +react@^18.2.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== @@ -31116,7 +29349,7 @@ read@1, read@~1.0.1: dependencies: mute-stream "~0.0.4" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@^2.3.8, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@^2.3.8, readable-stream@~2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -31159,13 +29392,6 @@ readable-stream@~1.0.31: isarray "0.0.1" string_decoder "~0.10.x" -readable-web-to-node-stream@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb" - integrity sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw== - dependencies: - readable-stream "^3.6.0" - readdir-scoped-modules@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" @@ -31259,13 +29485,6 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" -rechoir@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" - integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== - dependencies: - resolve "^1.20.0" - recursive-readdir@^2.2.2: version "2.2.3" resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.3.tgz#e726f328c0d69153bcabd5c322d3195252379372" @@ -31334,7 +29553,7 @@ reflect.getprototypeof@^1.0.4: globalthis "^1.0.3" which-builtin-type "^1.1.3" -reftools@^1.1.6, reftools@^1.1.9: +reftools@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/reftools/-/reftools-1.1.9.tgz#e16e19f662ccd4648605312c06d34e5da3a2b77e" integrity sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w== @@ -31657,9 +29876,9 @@ remark-rehype@^10.0.0: unified "^10.0.0" remark-rehype@^11.0.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-11.1.0.tgz#d5f264f42bcbd4d300f030975609d01a1697ccdc" - integrity sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g== + version "11.1.1" + resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-11.1.1.tgz#f864dd2947889a11997c0a2667cd6b38f685bca7" + integrity sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ== dependencies: "@types/hast" "^3.0.0" "@types/mdast" "^4.0.0" @@ -31746,11 +29965,6 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -replace-ext@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-2.0.0.tgz#9471c213d22e1bcc26717cd6e50881d88f812b06" - integrity sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug== - request-promise-core@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" @@ -31863,11 +30077,6 @@ resolve-from@5.0.0, resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve-from@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" - integrity sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw== - resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -31888,11 +30097,6 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== -resolve.exports@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" - integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== - resolve.exports@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" @@ -31933,7 +30137,7 @@ response-iterator@^0.2.6: resolved "https://registry.yarnpkg.com/response-iterator/-/response-iterator-0.2.6.tgz#249005fb14d2e4eeb478a3f735a28fd8b4c9f3da" integrity sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw== -responselike@1.0.2, responselike@^1.0.2: +responselike@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" integrity sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ== @@ -32121,6 +30325,17 @@ rollup-plugin-typescript2@^0.27.3: resolve "1.17.0" tslib "2.0.1" +rollup-plugin-typescript2@^0.36.0: + version "0.36.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.36.0.tgz#309564eb70d710412f5901344ca92045e180ed53" + integrity sha512-NB2CSQDxSe9+Oe2ahZbf+B4bh7pHwjV5L+RSYpCu7Q5ROuN94F9b6ioWwKfz3ueL3KTtmX4o2MUH2cgHDIEUsw== + dependencies: + "@rollup/pluginutils" "^4.1.2" + find-cache-dir "^3.3.2" + fs-extra "^10.0.0" + semver "^7.5.4" + tslib "^2.6.2" + rollup-plugin-visualizer@^5.9.2: version "5.12.0" resolved "https://registry.yarnpkg.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.12.0.tgz#661542191ce78ee4f378995297260d0c1efb1302" @@ -32148,9 +30363,9 @@ rollup@^1.32.1: acorn "^7.1.0" rollup@^3.29.4: - version "3.29.4" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.4.tgz#4d70c0f9834146df8705bfb69a9a19c9e1109981" - integrity sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw== + version "3.29.5" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.5.tgz#8a2e477a758b520fb78daf04bca4c522c1da8a54" + integrity sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w== optionalDependencies: fsevents "~2.3.2" @@ -32179,6 +30394,31 @@ rollup@^4.20.0: "@rollup/rollup-win32-x64-msvc" "4.21.3" fsevents "~2.3.2" +rollup@^4.22.4: + version "4.22.4" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.22.4.tgz#4135a6446671cd2a2453e1ad42a45d5973ec3a0f" + integrity sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A== + dependencies: + "@types/estree" "1.0.5" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.22.4" + "@rollup/rollup-android-arm64" "4.22.4" + "@rollup/rollup-darwin-arm64" "4.22.4" + "@rollup/rollup-darwin-x64" "4.22.4" + "@rollup/rollup-linux-arm-gnueabihf" "4.22.4" + "@rollup/rollup-linux-arm-musleabihf" "4.22.4" + "@rollup/rollup-linux-arm64-gnu" "4.22.4" + "@rollup/rollup-linux-arm64-musl" "4.22.4" + "@rollup/rollup-linux-powerpc64le-gnu" "4.22.4" + "@rollup/rollup-linux-riscv64-gnu" "4.22.4" + "@rollup/rollup-linux-s390x-gnu" "4.22.4" + "@rollup/rollup-linux-x64-gnu" "4.22.4" + "@rollup/rollup-linux-x64-musl" "4.22.4" + "@rollup/rollup-win32-arm64-msvc" "4.22.4" + "@rollup/rollup-win32-ia32-msvc" "4.22.4" + "@rollup/rollup-win32-x64-msvc" "4.22.4" + fsevents "~2.3.2" + rsvp@^4.8.4: version "4.8.5" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" @@ -32199,7 +30439,7 @@ rtlcss@^4.1.0: postcss "^8.4.21" strip-json-comments "^3.1.1" -run-async@^2.2.0, run-async@^2.4.0: +run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== @@ -32218,7 +30458,7 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^6.4.0, rxjs@^6.6.0: +rxjs@^6.6.0: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== @@ -32470,13 +30710,6 @@ section-matter@^1.0.0: extend-shallow "^2.0.1" kind-of "^6.0.0" -seek-bzip@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.6.tgz#35c4171f55a680916b52a07859ecf3b5857f21c4" - integrity sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ== - dependencies: - commander "^2.8.1" - select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -32502,19 +30735,7 @@ semver-diff@^4.0.0: dependencies: semver "^7.3.5" -semver-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-2.0.0.tgz#a93c2c5844539a770233379107b38c7b4ac9d338" - integrity sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw== - -semver-truncate@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/semver-truncate/-/semver-truncate-1.1.2.tgz#57f41de69707a62709a7e0104ba2117109ea47e8" - integrity sha512-V1fGg9i4CL3qesB6U0L6XAm4xOJiHmt4QAacazumuasc03BvtFGIMCduv01JWQ69Nv+JST9TqhSCiJoxoY031w== - dependencies: - semver "^5.3.0" - -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== @@ -32781,7 +31002,7 @@ shellac@^0.8.0: dependencies: reghex "^1.0.2" -shelljs@^0.8.3, shelljs@^0.8.5: +shelljs@0.8.5, shelljs@^0.8.3, shelljs@^0.8.5: version "0.8.5" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== @@ -33092,13 +31313,13 @@ snapdragon@^0.8.1: use "^3.1.0" socket.io-client@^4.5.1: - version "4.7.5" - resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.7.5.tgz#919be76916989758bdc20eec63f7ee0ae45c05b7" - integrity sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ== + version "4.8.0" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.8.0.tgz#2ea0302d0032d23422bd2860f78127a800cad6a2" + integrity sha512-C0jdhD5yQahMws9alf/yvtsMGTaIDBnZ8Rb5HU56svyq0l5LIrGzIDZZD5pHQlmzxLuU91Gz+VpQMKgCTNYtkw== dependencies: "@socket.io/component-emitter" "~3.1.0" debug "~4.3.2" - engine.io-client "~6.5.2" + engine.io-client "~6.6.1" socket.io-parser "~4.2.4" socket.io-parser@~4.2.4: @@ -33166,57 +31387,10 @@ solhint-plugin-prettier@^0.0.5: dependencies: prettier-linter-helpers "^1.0.0" -solhint@3.3.8: - version "3.3.8" - resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.3.8.tgz#b1773c881cfaf0b5008c78ad658a69603d3fa051" - integrity sha512-TkYyJ6uUJCaiqRKuhHhFuoAoyco9Ia+RDKhl3usjG/rkaNk8/LdLRla2Xln7MVdBTaPKNAU8ezTRSit50Yy4qw== - dependencies: - "@solidity-parser/parser" "^0.14.5" - ajv "^6.6.1" - antlr4 "4.7.1" - ast-parents "0.0.1" - chalk "^2.4.2" - commander "2.18.0" - cosmiconfig "^5.0.7" - eslint "^5.6.0" - fast-diff "^1.1.2" - glob "^7.1.3" - ignore "^4.0.6" - js-yaml "^3.12.0" - lodash "^4.17.11" - semver "^6.3.0" - optionalDependencies: - prettier "^1.14.3" - -solhint@^3.3.6: - version "3.6.2" - resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.6.2.tgz#2b2acbec8fdc37b2c68206a71ba89c7f519943fe" - integrity sha512-85EeLbmkcPwD+3JR7aEMKsVC9YrRSxd4qkXuMzrlf7+z2Eqdfm1wHWq1ffTuo5aDhoZxp2I9yF3QkxZOxOL7aQ== - dependencies: - "@solidity-parser/parser" "^0.16.0" - ajv "^6.12.6" - antlr4 "^4.11.0" - ast-parents "^0.0.1" - chalk "^4.1.2" - commander "^10.0.0" - cosmiconfig "^8.0.0" - fast-diff "^1.2.0" - glob "^8.0.3" - ignore "^5.2.4" - js-yaml "^4.1.0" - lodash "^4.17.21" - pluralize "^8.0.0" - semver "^7.5.2" - strip-ansi "^6.0.1" - table "^6.8.1" - text-table "^0.2.0" - optionalDependencies: - prettier "^2.8.3" - -solhint@^4.5.4: - version "4.5.4" - resolved "https://registry.yarnpkg.com/solhint/-/solhint-4.5.4.tgz#171cf33f46c36b8499efe60c0e425f6883a54e50" - integrity sha512-Cu1XiJXub2q1eCr9kkJ9VPv1sGcmj3V7Zb76B0CoezDOB9bu3DxKIFFH7ggCl9fWpEPD6xBmRLfZrYijkVmujQ== +solhint@5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/solhint/-/solhint-5.0.3.tgz#b57f6d2534fe09a60f9db1b92e834363edd1cbde" + integrity sha512-OLCH6qm/mZTCpplTXzXTJGId1zrtNuDYP5c2e6snIv/hdRVxPfBBz/bAlL91bY/Accavkayp2Zp2BaDSrLVXTQ== dependencies: "@solidity-parser/parser" "^0.18.0" ajv "^6.12.6" @@ -33277,30 +31451,11 @@ sonic-boom@^3.7.0: dependencies: atomic-sleep "^1.0.0" -sort-css-media-queries@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/sort-css-media-queries/-/sort-css-media-queries-2.1.0.tgz#7c85e06f79826baabb232f5560e9745d7a78c4ce" - integrity sha512-IeWvo8NkNiY2vVYdPa27MCQiR0MN0M80johAYFVxWWXQ44KU84WNxjslwBHmc/7ZL2ccwkM7/e6S5aiKZXm7jA== - sort-css-media-queries@2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz#aa33cf4a08e0225059448b6c40eddbf9f1c8334c" integrity sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA== -sort-keys-length@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sort-keys-length/-/sort-keys-length-1.0.1.tgz#9cb6f4f4e9e48155a6aa0671edd336ff1479a188" - integrity sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw== - dependencies: - sort-keys "^1.0.0" - -sort-keys@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" - integrity sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg== - dependencies: - is-plain-obj "^1.0.0" - sort-keys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" @@ -33531,15 +31686,6 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== -squeak@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/squeak/-/squeak-1.3.0.tgz#33045037b64388b567674b84322a6521073916c3" - integrity sha512-YQL1ulInM+ev8nXX7vfXsCsDh6IqXlrremc1hzi77776BtpWgYJUMto3UM05GSAaGzJgWekszjoKDrVNB5XG+A== - dependencies: - chalk "^1.0.0" - console-stream "^0.1.1" - lpad-align "^1.0.1" - srcset@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/srcset/-/srcset-4.0.0.tgz#336816b665b14cd013ba545b6fe62357f86e65f4" @@ -33838,7 +31984,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.1.0, string-width@^2.1.1: +"string-width@^1.0.2 || 2", string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -34039,13 +32185,6 @@ strip-bom@^4.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== -strip-dirs@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5" - integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g== - dependencies: - is-natural-number "^4.0.1" - strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" @@ -34092,23 +32231,11 @@ strip-json-comments@4.0.0: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-4.0.0.tgz#6fd3a79f1b956905483769b0bf66598b8f87da50" integrity sha512-LzWcbfMbAsEDTRmhjWIioe8GcDRl0fa35YMXFoJKDdiD/quGFmjJjdgPjFJJNwCMaLyQqFIDqCdHD2V4HfLgYA== -strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: +strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== -strip-outer@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631" - integrity sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg== - dependencies: - escape-string-regexp "^1.0.2" - -strnum@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" - integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== - strong-log-transformer@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" @@ -34118,14 +32245,6 @@ strong-log-transformer@^2.1.0: minimist "^1.2.0" through "^2.3.4" -strtok3@^6.2.4: - version "6.3.0" - resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-6.3.0.tgz#358b80ffe6d5d5620e19a073aa78ce947a90f9a0" - integrity sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw== - dependencies: - "@tokenizer/token" "^0.3.0" - peek-readable "^4.1.0" - stubborn-fs@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/stubborn-fs/-/stubborn-fs-1.2.5.tgz#e5e244223166921ddf66ed5e062b6b3bf285bfd2" @@ -34235,6 +32354,21 @@ superagent@^8.1.2: qs "^6.11.0" semver "^7.3.8" +superagent@^9.0.1: + version "9.0.2" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-9.0.2.tgz#a18799473fc57557289d6b63960610e358bdebc1" + integrity sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w== + dependencies: + component-emitter "^1.3.0" + cookiejar "^2.1.4" + debug "^4.3.4" + fast-safe-stringify "^2.1.1" + form-data "^4.0.0" + formidable "^3.5.1" + methods "^1.1.2" + mime "2.6.0" + qs "^6.11.0" + superstruct@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-1.0.4.tgz#0adb99a7578bd2f1c526220da6571b2d485d91ca" @@ -34248,6 +32382,14 @@ supertest@^6.3.3: methods "^1.1.2" superagent "^8.1.2" +supertest@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/supertest/-/supertest-7.0.0.tgz#cac53b3d6872a0b317980b2b0cfa820f09cd7634" + integrity sha512-qlsr7fIC0lSddmA3tzojvzubYxvlGtzumcdHgPwbFWMISQwL22MhM2Y3LNt+6w9Yyx7559VW5ab70dgphm8qQA== + dependencies: + methods "^1.1.2" + superagent "^9.0.1" + supports-color@8.1.1, supports-color@^8.0.0, supports-color@^8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" @@ -34255,11 +32397,6 @@ supports-color@8.1.1, supports-color@^8.0.0, supports-color@^8.1.1: dependencies: has-flag "^4.0.0" -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g== - supports-color@^3.1.0: version "3.2.3" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" @@ -34320,7 +32457,7 @@ svg-tags@1: resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA== -svgo@^2.5.0, svgo@^2.7.0, svgo@^2.8.0: +svgo@^2.7.0: version "2.8.0" resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== @@ -34444,6 +32581,16 @@ symbol.prototype.description@^1.0.0: has-symbols "^1.0.3" object.getownpropertydescriptors "^2.1.7" +synapse-constants@1.3.22: + version "1.3.22" + resolved "https://registry.yarnpkg.com/synapse-constants/-/synapse-constants-1.3.22.tgz#e885e12870909b62b1484db007868b8b59648dd7" + integrity sha512-PnEuKh8LBujoqOW+VzbQPkAvDld0aBxQUcnN7f0HeSxoF43I5gHyTYngm86qmgf3D+9rPplrigYtVscIBR8NfQ== + dependencies: + "@codecov/webpack-plugin" "^0.0.1-beta.10" + copyfiles "^2.4.1" + ethers "5.7.2" + lodash "^4.17.21" + synchronous-promise@^2.0.15: version "2.0.17" resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.17.tgz#38901319632f946c982152586f2caf8ddc25c032" @@ -34551,19 +32698,6 @@ tar-fs@^2.0.0, tar-fs@^2.1.1: pump "^3.0.0" tar-stream "^2.1.4" -tar-stream@^1.5.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" - integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== - dependencies: - bl "^1.0.0" - buffer-alloc "^1.2.0" - end-of-stream "^1.0.0" - fs-constants "^1.0.0" - readable-stream "^2.3.0" - to-buffer "^1.1.1" - xtend "^4.0.0" - tar-stream@^2.1.4: version "2.2.0" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" @@ -34661,14 +32795,6 @@ temp-write@^4.0.0: temp-dir "^1.0.0" uuid "^3.3.2" -tempfile@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/tempfile/-/tempfile-2.0.0.tgz#6b0446856a9b1114d1856ffcbe509cccb0977265" - integrity sha512-ZOn6nJUgvgC09+doCEF3oB+r3ag7kUvlsXEGX069QRD60p+P3uP7XG9N2/at+EyIRGSN//ZY3LyEotA1YpmjuA== - dependencies: - temp-dir "^1.0.0" - uuid "^3.0.1" - term-size@^2.1.0: version "2.2.1" resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54" @@ -34826,7 +32952,7 @@ time-span@4.0.0: dependencies: convert-hrtime "^3.0.0" -timed-out@^4.0.0, timed-out@^4.0.1: +timed-out@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" integrity sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA== @@ -34880,11 +33006,6 @@ to-arraybuffer@^1.0.0: resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" integrity sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA== -to-buffer@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" - integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== - to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -34942,14 +33063,6 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== -token-types@^4.1.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/token-types/-/token-types-4.2.1.tgz#0f897f03665846982806e138977dbe72d44df753" - integrity sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ== - dependencies: - "@tokenizer/token" "^0.3.0" - ieee754 "^1.2.1" - totalist@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" @@ -35025,13 +33138,6 @@ trim-newlines@^3.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== -trim-repeated@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21" - integrity sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg== - dependencies: - escape-string-regexp "^1.0.2" - trim-trailing-lines@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz#bd4abbec7cc880462f10b2c8b5ce1d8d1ec7c2c0" @@ -35402,6 +33508,11 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tunnel@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" + integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== + tweetnacl-util@^0.15.1: version "0.15.1" resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" @@ -35734,14 +33845,6 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" -unbzip2-stream@^1.0.9: - version "1.4.3" - resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" - integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== - dependencies: - buffer "^5.2.1" - through "^2.3.8" - unc-path-regex@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" @@ -35774,18 +33877,13 @@ undici@5.26.5: dependencies: "@fastify/busboy" "^2.0.0" -undici@^5.14.0, undici@^5.28.4: +undici@^5.14.0, undici@^5.25.4, undici@^5.28.4: version "5.28.4" resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068" integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== dependencies: "@fastify/busboy" "^2.0.0" -undici@^6.19.5: - version "6.19.8" - resolved "https://registry.yarnpkg.com/undici/-/undici-6.19.8.tgz#002d7c8a28f8cc3a44ff33c3d4be4d85e15d40e1" - integrity sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g== - unenv@^1.9.0: version "1.10.0" resolved "https://registry.yarnpkg.com/unenv/-/unenv-1.10.0.tgz#c3394a6c6e4cfe68d699f87af456fe3f0db39571" @@ -36238,13 +34336,6 @@ url-loader@^4.1.1: mime-types "^2.1.27" schema-utils "^3.0.0" -url-parse-lax@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" - integrity sha512-BVA4lR5PIviy2PMseNd2jbFQ+jwSwQGdJejf5ctd1rEXt0Ypd7yanUK9+lYechVlN5VaTJGsu2U/3MDDu6KgBA== - dependencies: - prepend-http "^1.0.1" - url-parse-lax@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" @@ -36257,11 +34348,6 @@ url-set-query@^1.0.0: resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" integrity sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg== -url-to-options@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" - integrity sha512-0kQLIzG4fdk/G5NONku64rSH/x32NOA39LVQqlK8Le6lvTF6GGRJpqaQFGgU+CLwySIqBSMdwYM0sYcW9f6P4A== - url@^0.11.0: version "0.11.4" resolved "https://registry.yarnpkg.com/url/-/url-0.11.4.tgz#adca77b3562d56b72746e76b330b7f27b6721f3c" @@ -36429,7 +34515,7 @@ uuid@8.3.2, uuid@^8.0.0, uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -uuid@^3.0.1, uuid@^3.3.2: +uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== @@ -37012,7 +35098,7 @@ web3-core@1.7.4: web3-core-requestmanager "1.7.4" web3-utils "1.7.4" -web3-core@^4.3.0, web3-core@^4.4.0, web3-core@^4.5.0, web3-core@^4.5.1: +web3-core@^4.4.0, web3-core@^4.5.0, web3-core@^4.5.1: version "4.5.1" resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-4.5.1.tgz#aba57c7f925fc2d72e2109eef5ff0ecaa4482b86" integrity sha512-mFMOO/IWdKsLL1o2whh3oJ0LCG9P3l5c4lpiMoVsVln3QXh/B0Gf8gW3aY8S+Ixm0OHyzFDXJVc2CodxqmI4Gw== @@ -37028,6 +35114,22 @@ web3-core@^4.3.0, web3-core@^4.4.0, web3-core@^4.5.0, web3-core@^4.5.1: optionalDependencies: web3-providers-ipc "^4.0.7" +web3-core@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-4.6.0.tgz#1b3e88ed35142b4d6fcbc7737e1d71943f99cf45" + integrity sha512-j8uQ/7zSwpmLClMMeZb736Ok3V4cWSd0dnd29jkd10d1pedi32r+hSAgycxSJLLWtPHOzMBIXUjj3TF/IAClVQ== + dependencies: + web3-errors "^1.3.0" + web3-eth-accounts "^4.2.1" + web3-eth-iban "^4.0.7" + web3-providers-http "^4.2.0" + web3-providers-ws "^4.0.8" + web3-types "^1.8.0" + web3-utils "^4.3.1" + web3-validator "^2.0.6" + optionalDependencies: + web3-providers-ipc "^4.0.7" + web3-errors@^1.1.3, web3-errors@^1.2.0, web3-errors@^1.2.1, web3-errors@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/web3-errors/-/web3-errors-1.3.0.tgz#504e4d3218899df108856940087a8022d6688d74" @@ -37062,6 +35164,17 @@ web3-eth-abi@^4.2.3: web3-utils "^4.3.1" web3-validator "^2.0.6" +web3-eth-abi@^4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-4.2.4.tgz#b66f4b067ba06c0aecc013e98a4d717547ab8174" + integrity sha512-FGoj/ENm/Iq3+6myJyiDCwbFkha9ZCx2fRdiIdw3mp7S4lgu+ay3EVzQPRxJjNBm09UEfxB9yoSAPKj9Z3Mbxg== + dependencies: + abitype "0.7.1" + web3-errors "^1.3.0" + web3-types "^1.8.0" + web3-utils "^4.3.1" + web3-validator "^2.0.6" + web3-eth-accounts@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.10.0.tgz#2942beca0a4291455f32cf09de10457a19a48117" @@ -37244,16 +35357,16 @@ web3-eth-personal@1.7.4: web3-utils "1.7.4" web3-eth-personal@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-4.0.8.tgz#b51628c560de550ca8b354fa784f9556aae6065c" - integrity sha512-sXeyLKJ7ddQdMxz1BZkAwImjqh7OmKxhXoBNF3isDmD4QDpMIwv/t237S3q4Z0sZQamPa/pHebJRWVuvP8jZdw== + version "4.1.0" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-4.1.0.tgz#f5b506a4570bf1241d1db2de12cb413ea0bb4486" + integrity sha512-RFN83uMuvA5cu1zIwwJh9A/bAj0OBxmGN3tgx19OD/9ygeUZbifOL06jgFzN0t+1ekHqm3DXYQM8UfHpXi7yDQ== dependencies: - web3-core "^4.3.0" - web3-eth "^4.3.1" - web3-rpc-methods "^1.1.3" - web3-types "^1.3.0" - web3-utils "^4.0.7" - web3-validator "^2.0.3" + web3-core "^4.6.0" + web3-eth "^4.9.0" + web3-rpc-methods "^1.3.0" + web3-types "^1.8.0" + web3-utils "^4.3.1" + web3-validator "^2.0.6" web3-eth@1.10.0: version "1.10.0" @@ -37291,7 +35404,7 @@ web3-eth@1.7.4: web3-net "1.7.4" web3-utils "1.7.4" -web3-eth@^4.3.1, web3-eth@^4.8.0, web3-eth@^4.8.2: +web3-eth@^4.8.0, web3-eth@^4.8.2: version "4.8.2" resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-4.8.2.tgz#5459aff239a6f27816a522570bdd86732856ef0f" integrity sha512-DLV/fIMG6gBp/B0gv0+G4FzxZ4YCDQsY3lzqqv7avwh3uU7/O27aifCUcFd7Ye+3ixTqCjAvLEl9wYSeyG3zQw== @@ -37308,6 +35421,23 @@ web3-eth@^4.3.1, web3-eth@^4.8.0, web3-eth@^4.8.2: web3-utils "^4.3.1" web3-validator "^2.0.6" +web3-eth@^4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-4.9.0.tgz#324403d913cc29bcae6cc1ad50a6defeb762828a" + integrity sha512-lE+5rQUkQq1Mzf3uZ/tlay8nvMyC/CmaRFRFQ015OZuvSrRr/byZhhkzY5ZWkIetESTMqfWapu67yeHebcHxwA== + dependencies: + setimmediate "^1.0.5" + web3-core "^4.6.0" + web3-errors "^1.3.0" + web3-eth-abi "^4.2.4" + web3-eth-accounts "^4.2.1" + web3-net "^4.1.0" + web3-providers-ws "^4.0.8" + web3-rpc-methods "^1.3.0" + web3-types "^1.8.0" + web3-utils "^4.3.1" + web3-validator "^2.0.6" + web3-net@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.10.0.tgz#be53e7f5dafd55e7c9013d49c505448b92c9c97b" @@ -37419,7 +35549,7 @@ web3-providers-ws@^4.0.8: web3-utils "^4.3.1" ws "^8.17.1" -web3-rpc-methods@^1.1.3, web3-rpc-methods@^1.3.0: +web3-rpc-methods@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/web3-rpc-methods/-/web3-rpc-methods-1.3.0.tgz#d5ee299a69389d63822d354ddee2c6a121a6f670" integrity sha512-/CHmzGN+IYgdBOme7PdqzF+FNeMleefzqs0LVOduncSaqsppeOEoskLXb2anSpzmQAP3xZJPaTrkQPWSJMORig== @@ -37465,6 +35595,11 @@ web3-types@^1.3.0, web3-types@^1.6.0, web3-types@^1.7.0: resolved "https://registry.yarnpkg.com/web3-types/-/web3-types-1.7.0.tgz#9945fa644af96b20b1db18564aff9ab8db00df59" integrity sha512-nhXxDJ7a5FesRw9UG5SZdP/C/3Q2EzHGnB39hkAV+YGXDMgwxBXFWebQLfEzZzuArfHnvC0sQqkIHNwSKcVjdA== +web3-types@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/web3-types/-/web3-types-1.8.0.tgz#d2151fd9e87d711ef5a13079885665b458243e46" + integrity sha512-Z51wFLPGhZM/1uDxrxE8gzju3t2aEdRGn+YmLX463id5UjTuMEmP/9in1GFjqrsPB3m86czs8RnGBUt3ovueMw== + web3-utils@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.10.0.tgz#ca4c1b431a765c14ac7f773e92e0fd9377ccf578" @@ -37638,25 +35773,6 @@ webpack-bundle-analyzer@^4.9.0: sirv "^2.0.3" ws "^7.3.1" -webpack-cli@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b" - integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== - dependencies: - "@discoveryjs/json-ext" "^0.5.0" - "@webpack-cli/configtest" "^2.1.1" - "@webpack-cli/info" "^2.0.2" - "@webpack-cli/serve" "^2.0.5" - colorette "^2.0.14" - commander "^10.0.1" - cross-spawn "^7.0.3" - envinfo "^7.7.3" - fastest-levenshtein "^1.0.12" - import-local "^3.0.2" - interpret "^3.1.1" - rechoir "^0.8.0" - webpack-merge "^5.7.3" - webpack-dev-middleware@^3.7.3: version "3.7.3" resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5" @@ -37749,7 +35865,7 @@ webpack-log@^2.0.0: ansi-colors "^3.0.0" uuid "^3.3.2" -webpack-merge@^5.7.3, webpack-merge@^5.9.0: +webpack-merge@^5.9.0: version "5.10.0" resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== @@ -37766,7 +35882,7 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: source-list-map "^2.0.0" source-map "~0.6.1" -webpack-sources@^3.2.2, webpack-sources@^3.2.3: +webpack-sources@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== @@ -37817,7 +35933,7 @@ webpack@4: watchpack "^1.7.4" webpack-sources "^1.4.1" -"webpack@>=4.43.0 <6.0.0", webpack@^5.61.0, webpack@^5.88.1, webpack@^5.89.0, webpack@^5.9.0: +"webpack@>=4.43.0 <6.0.0", webpack@^5.61.0, webpack@^5.88.1, webpack@^5.9.0: version "5.94.0" resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.94.0.tgz#77a6089c716e7ab90c1c67574a28da518a20970f" integrity sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg== @@ -37889,23 +36005,11 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5: dependencies: iconv-lite "0.4.24" -whatwg-encoding@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" - integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== - dependencies: - iconv-lite "0.6.3" - whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-mimetype@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" - integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== - whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -38159,7 +36263,7 @@ write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write-file-atomic@^4.0.1, write-file-atomic@^4.0.2: +write-file-atomic@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== @@ -38333,10 +36437,10 @@ xmlchars@^2.1.1: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xmlhttprequest-ssl@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67" - integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A== +xmlhttprequest-ssl@~2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.1.tgz#0d045c3b2babad8e7db1af5af093f5d0d60df99a" + integrity sha512-ptjR8YSJIXoA3Mbv5po7RtSYHO6mZr8s7i5VGmEk7QY2pQWyT1o0N+W1gKbOyJPUCGXGnuw0wqe8f0L6Y0ny7g== xmlhttprequest@1.8.0: version "1.8.0" @@ -38494,7 +36598,7 @@ yauzl-promise@2.1.3: yauzl "^2.9.1" yauzl-clone "^1.0.4" -yauzl@^2.4.2, yauzl@^2.9.1: +yauzl@^2.9.1: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==