diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..f17a1b9
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,8 @@
+DEPLOYER_PK=
+OWNER_ADDR=
+CREATE2_SALT=
+UNICHAIN_SEPOLIA_RPC=
+OPTIMISM_SEPOLIA_RPC=
+UNICHAIN_EXPLORER_API=
+OPTIMISM_EXPLORER_API=
+OPTIMISM_EXPLORER_API_KEY=
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..52031de
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+*.sol linguist-language=Solidity
diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
new file mode 100644
index 0000000..ca00c44
--- /dev/null
+++ b/.github/workflows/coverage.yml
@@ -0,0 +1,73 @@
+name: Coverage Check
+
+on: [push]
+
+env:
+ COVERAGE_SENSITIVITY_PERCENT: 1
+
+jobs:
+ upload-coverage:
+ name: Upload Coverage
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install Foundry
+ uses: foundry-rs/foundry-toolchain@v1
+ with:
+ version: nightly
+
+ - name: Use Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 22.x
+ cache: 'yarn'
+
+ - name: Install dependencies
+ run: yarn --frozen-lockfile --network-concurrency 1
+
+ - name: Run coverage
+ shell: bash
+ run: yarn coverage
+
+ - name: Setup LCOV
+ uses: hrishikesh-kadam/setup-lcov@v1
+
+ - name: Filter directories
+ run: lcov --remove lcov.info 'test/*' 'script/*' --output-file lcovNew.info --rc lcov_branch_coverage=1 --rc derive_function_end_line=0 --ignore-errors unused
+
+ - name: Capture coverage output
+ id: new-coverage
+ uses: zgosalvez/github-actions-report-lcov@v4
+ with:
+ coverage-files: lcovNew.info
+
+ - name: Retrieve previous coverage
+ uses: actions/download-artifact@v4
+ with:
+ name: coverage.info
+ continue-on-error: true
+
+ - name: Check if a previous coverage exists
+ run: |
+ if [ ! -f coverage.info ]; then
+ echo "Artifact not found. Initializing at 0"
+ echo "0" >> coverage.info
+ fi
+
+ - name: Compare previous coverage
+ run: |
+ old=$(cat coverage.info)
+ new=$(( ${{ steps.new-coverage.outputs.total-coverage }} + ${{ env.COVERAGE_SENSITIVITY_PERCENT }} ))
+ if [ "$new" -lt "$old" ]; then
+ echo "Coverage decreased from $old to $new"
+ exit 1
+ fi
+ mv lcovNew.info coverage.info
+
+ - name: Upload the new coverage
+ uses: actions/upload-artifact@v4
+ with:
+ name: coverage.info
+ path: ./coverage.info
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..ca00c44
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,73 @@
+name: Coverage Check
+
+on: [push]
+
+env:
+ COVERAGE_SENSITIVITY_PERCENT: 1
+
+jobs:
+ upload-coverage:
+ name: Upload Coverage
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install Foundry
+ uses: foundry-rs/foundry-toolchain@v1
+ with:
+ version: nightly
+
+ - name: Use Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 22.x
+ cache: 'yarn'
+
+ - name: Install dependencies
+ run: yarn --frozen-lockfile --network-concurrency 1
+
+ - name: Run coverage
+ shell: bash
+ run: yarn coverage
+
+ - name: Setup LCOV
+ uses: hrishikesh-kadam/setup-lcov@v1
+
+ - name: Filter directories
+ run: lcov --remove lcov.info 'test/*' 'script/*' --output-file lcovNew.info --rc lcov_branch_coverage=1 --rc derive_function_end_line=0 --ignore-errors unused
+
+ - name: Capture coverage output
+ id: new-coverage
+ uses: zgosalvez/github-actions-report-lcov@v4
+ with:
+ coverage-files: lcovNew.info
+
+ - name: Retrieve previous coverage
+ uses: actions/download-artifact@v4
+ with:
+ name: coverage.info
+ continue-on-error: true
+
+ - name: Check if a previous coverage exists
+ run: |
+ if [ ! -f coverage.info ]; then
+ echo "Artifact not found. Initializing at 0"
+ echo "0" >> coverage.info
+ fi
+
+ - name: Compare previous coverage
+ run: |
+ old=$(cat coverage.info)
+ new=$(( ${{ steps.new-coverage.outputs.total-coverage }} + ${{ env.COVERAGE_SENSITIVITY_PERCENT }} ))
+ if [ "$new" -lt "$old" ]; then
+ echo "Coverage decreased from $old to $new"
+ exit 1
+ fi
+ mv lcovNew.info coverage.info
+
+ - name: Upload the new coverage
+ uses: actions/upload-artifact@v4
+ with:
+ name: coverage.info
+ path: ./coverage.info
\ No newline at end of file
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 0000000..5351500
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,92 @@
+name: CI
+
+on: [push]
+
+concurrency:
+ group: ${{github.workflow}}-${{github.ref}}
+ cancel-in-progress: true
+
+env:
+ UNICHAIN_SEPOLIA_RPC: ${{ secrets.UNICHAIN_SEPOLIA_RPC }}
+ OPTIMISM_SEPOLIA_RPC: ${{ secrets.OPTIMISM_SEPOLIA_RPC }}
+
+jobs:
+ unit-tests:
+ name: Run Unit Tests
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install Foundry
+ uses: foundry-rs/foundry-toolchain@v1
+ with:
+ version: nightly
+
+ - name: Use Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 22.x
+ cache: 'yarn'
+
+ - name: Install dependencies
+ run: yarn --frozen-lockfile --network-concurrency 1
+
+ - name: Precompile using 0.8.25 and via-ir=false
+ run: yarn build
+
+ - name: Run tests
+ shell: bash
+ run: yarn test:unit
+
+ integration-tests:
+ name: Run Integration Tests
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install Foundry
+ uses: foundry-rs/foundry-toolchain@v1
+ with:
+ version: nightly
+
+ - name: Use Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 22.x
+ cache: 'yarn'
+
+ - name: Install dependencies
+ run: yarn --frozen-lockfile --network-concurrency 1
+
+ - name: Precompile using 0.8.25 and via-ir=false
+ run: yarn build
+
+ - name: Run tests
+ run: yarn test:integration
+
+ lint:
+ name: Lint Commit Messages
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - uses: wagoid/commitlint-github-action@v5
+
+ - name: Install Foundry
+ uses: foundry-rs/foundry-toolchain@v1
+ with:
+ version: nightly
+
+ - name: Use Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 22.x
+ cache: 'yarn'
+
+ - name: Install dependencies
+ run: yarn --frozen-lockfile --network-concurrency 1
+
+ - run: yarn lint:check
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..51c525f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,26 @@
+# General
+yarn-error.log
+node_modules
+.DS_STORE
+.vscode
+
+# Foundry files
+cache
+out-via-ir
+
+# Config files
+.env
+.notes
+
+# Avoid ignoring gitkeep
+!/**/.gitkeep
+
+# Keep the latest deployment only
+broadcast/*/*/*
+
+# Out dir
+out
+crytic-export
+
+# Echidna corpus
+test/invariants/fuzz/echidna_coverage
\ No newline at end of file
diff --git a/.husky/.gitignore b/.husky/.gitignore
new file mode 100644
index 0000000..31354ec
--- /dev/null
+++ b/.husky/.gitignore
@@ -0,0 +1 @@
+_
diff --git a/.husky/commit-msg b/.husky/commit-msg
new file mode 100755
index 0000000..d468455
--- /dev/null
+++ b/.husky/commit-msg
@@ -0,0 +1 @@
+npx --no-install commitlint --edit $1
\ No newline at end of file
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100755
index 0000000..c8d494d
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1,4 @@
+# 1. Build the contracts
+# 2. Stage build output
+# 3. Lint and stage style improvements
+yarn build && npx lint-staged
\ No newline at end of file
diff --git a/.solhint.json b/.solhint.json
new file mode 100644
index 0000000..57dca4f
--- /dev/null
+++ b/.solhint.json
@@ -0,0 +1,26 @@
+{
+ "extends": "solhint:recommended",
+ "rules": {
+ "compiler-version": [
+ "warn"
+ ],
+ "quotes": "off",
+ "func-visibility": [
+ "warn",
+ {
+ "ignoreConstructors": true
+ }
+ ],
+ "no-inline-assembly": "off",
+ "no-empty-blocks": "off",
+ "private-vars-leading-underscore": [
+ "warn",
+ {
+ "strict": false
+ }
+ ],
+ "ordering": "warn",
+ "avoid-low-level-calls": "off",
+ "named-parameters-mapping": "warn"
+ }
+}
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..99dd94e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,8 @@
+The MIT License (MIT)
+Copyright © 2023 Wonderland
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..94b7d3f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,236 @@
+# Mock Interop Sepolia
+
+
+The Interoperability Mock System serves as a sandbox for emulating cross-chain interactions. The system replicates how messages move across chains and verifies the integrity of this communication through permissioned relayers, which are trusted entities managed by the contract owner(s).
+
+
+## Disclaimer
+
+The following contracts are __not intended for use in production environments__. Please exercise caution and conduct thorough due diligence before utilizing them.
+
+## Setup
+
+1. Install Foundry by following the instructions from [their repository](https://github.com/foundry-rs/foundry#installation).
+2. Copy the `.env.example` file to `.env` and fill in the variables.
+3. Install the dependencies by running: `yarn install`. In case there is an error with the commands, run `foundryup` and try them again.
+
+## Build
+
+The default way to build the code is suboptimal but fast, you can run it via:
+
+```bash
+yarn build
+```
+
+In order to build a more optimized code ([via IR](https://docs.soliditylang.org/en/v0.8.15/ir-breaking-changes.html#solidity-ir-based-codegen-changes)), run:
+
+```bash
+yarn build:optimized
+```
+
+## Running tests
+
+Unit tests should be isolated from any externalities, while Integration usually run in a fork of the blockchain. In this boilerplate you will find example of both.
+
+In order to run both unit and integration tests, run:
+
+```bash
+yarn test
+```
+
+In order to just run unit tests, run:
+
+```bash
+yarn test:unit
+```
+
+In order to run unit tests and run way more fuzzing than usual (5x), run:
+
+```bash
+yarn test:unit:deep
+```
+
+In order to just run integration tests, run:
+
+```bash
+yarn test:integration
+```
+
+In order to check your current code coverage, run:
+
+```bash
+yarn coverage
+```
+
+
+
+## Deploy & verify
+
+Note: In order to deploy contracts with the same address in every chain is important to use the same `SALT` and `DEPLOYER` for every deployment.
+
+### OP Sepolia
+
+```bash
+yarn deploy:optimism:sepolia
+```
+
+### Unichain Sepolia
+
+```bash
+yarn deploy:unichain:sepolia
+```
+
+## Deployed Contracts by Wonderland
+
+### 🔴 Optimism Sepolia
+
+**CrossL2Inbox**: https://sepolia-optimism.etherscan.io/address/0x20fce9a9c640bd99a0edb14613356cf8d54c2bb3
+
+**L2ToL2CrossDomainMessenger**: https://sepolia-optimism.etherscan.io/address/0x16cb4762e5f36f0cf5d8503a0771f048c9f8c460
+
+### 🦄 Unichain Sepolia
+
+**CrossL2Inbox**: https://unichain-sepolia.blockscout.com/address/0x20FcE9A9c640bd99a0eDB14613356cf8D54C2BB3
+
+**L2ToL2CrossDomainMessenger:** https://unichain-sepolia.blockscout.com/address/0x16cB4762e5f36f0cF5D8503A0771f048C9f8c460
+
+### 🥎 Mode Sepolia
+
+**CrossL2Inbox:** https://sepolia.explorer.mode.network/address/0x20FcE9A9c640bd99a0eDB14613356cf8D54C2BB3
+
+**L2ToL2CrossDomainMessenger:** https://sepolia.explorer.mode.network/address/0x16cB4762e5f36f0cF5D8503A0771f048C9f8c460
+
+## Upgrade
+
+Contracts are UUPS proxies and can be upgraded calling the following function from Owner's account:
+
+```solidity
+function upgradeToAndCall(address newImplementation, bytes memory data)
+```
+
+
+## Usage Guide
+
+### Messaging
+
+**With sponsored relay**
+
+```mermaid
+sequenceDiagram
+ actor A1 as Alice
+ participant P1 as OP CDM
+ participant P2 as Hash Relayer
+ participant P3 as Message Relayer
+ participant P4 as Uni CrossL2Inbox
+ participant P5 as Uni CDM
+ actor A2 as Bob
+
+ A1 ->> P1: sendMessage()
+ P1 --> P2: SentMessage()
+ P2 ->> P4: postValidHash()
+ P3 ->> P5: relayMessage()
+ P5 ->> P4: validateMessage()
+
+ P5 ->> A2: call()
+```
+
+**Without sponsored relay**
+
+```mermaid
+sequenceDiagram
+ actor A1 as Alice
+ participant P1 as OP CDM
+ participant P2 as Hash Relayer
+ participant P4 as Uni CrossL2Inbox
+ participant P5 as Uni CDM
+ actor A2 as Bob
+
+ A1 ->> P1: sendMessage()
+ P1 --> P2: SentMessage()
+ P2 ->> P4: postValidHash()
+ A1 ->> P5: relayMessage()
+ P5 ->> P4: validateMessage()
+ P5 ->> A2: call()
+```
+
+As illustrated in the diagram, four core components are required:
+
+- `L2ToL2CrossDomainMessenger` emits a `SentMessage` event.
+- `Hash Relayer` listens for the `SentMessage` event and posts a `hash` on `CrossL2Inbox`.
+- `Message Relayer` or an EOA calls `relayMessage()` on `L2ToL2CrossDomainMessenger`.
+- `CrossL2Inbox` is used by `L2ToL2CrossDomainMessenger` to verify that the message is valid.
+
+**Sending Message**
+
+On `L2ToL2CrossDomainMessanger` from Origin Chain:
+
+1. User calls `sendMessage(uint256 _destination, address _target, bytes calldata _message) external returns (bytes32 hash_)`
+ 1.1. `_destination` is the ID of the destination chain.
+ 1.2. `_target` is the address that will receive the `_message`.
+ 1.3. `_message` encoded call.
+2. Contract emits `SentMessage(_destination, _target, nonce, **msg.sender**, _message);`
+
+**Posting Hash on Destination**
+
+With a service listening at `L2ToL2CrossDomainMessenger` events from origin chain, when a `SentMessage` event is emitted this service should use the log topics, log data and blockchain data to generate a hash.
+
+1. Generate hash
+
+ **Message Hash:** is composed by log topics encoded, log data encoded and the `keccak256` of the `SentMessage` signature.
+
+ ```solidity
+ keccak256(
+ abi.encodePacked(
+ keccak256('SentMessage(uint256,address,uint256,address,bytes)'),
+ abi.encode(_destination, _target, _nonce),
+ abi.encode(_sender, _message)
+ )
+ );
+ ```
+
+ ```bash
+ > cast keccak256 'SentMessage(uint256,address,uint256,address,bytes)'
+ 0x382409ac69001e11931a28435afef442cbfd20d9891907e8fa373ba7d351f320
+ ```
+
+ **Identifier:** is a struct that uses blockchain data. `origin` in this case the address of the `L2ToL2CrossDomainMessenger`.
+
+ ```solidity
+ struct Identifier {
+ address origin;
+ uint256 blockNumber;
+ uint256 logIndex;
+ uint256 timestamp;
+ uint256 chainId;
+ }
+ ```
+
+ **Hash:** this is the hash that will be posted on the destination chain.
+
+ ```solidity
+ bytes32 _hash = keccak256(abi.encode(_id, _msgHash));
+ ```
+
+2. Call `postValidHash()` in `CrossL2Inbox` using the relayer’s address that has been previously enabled in `CrossL2Inbox`.
+
+**Relaying Message**
+
+Here we have two options:
+
+1. With sponsored relay, once the hash is posted, a Message Relayer that is listening `RegisteredHash()` events can call `relayMessage(Identifier calldata _id, bytes calldata _sentMessage)` function on the destination chain `L2ToL2CrossDomainMessenger` using the same data that was used to post the hash.
+2. Without sponsored relay, the user can call `relayMessage(Identifier calldata _id, bytes calldata _sentMessage)` in the same way the relayer does.
+
+It's important to note that whether or not relaying is sponsored, the system will always require that the relayer posts the hash before the relay happens.
+
+---
+
+### Enable a Relayer
+
+Owner of `CrossL2Inbox`, as the system administrator, can approve new relayers to join the list of trusted entities for message transmission across the network by calling `enableRelayer()` function.
+
+### Disable a Relayer
+
+If a relayer no longer meets the trust requirements or wants to stop acting as one, Owner of `CrossL2Inbox` has the authority to revoke its access and remove it from the approved relayers list.
+
+## Licensing
+The primary license for the boilerplate is MIT, see [`LICENSE`](https://github.com/defi-wonderland/mock-interop/blob/main/LICENSE)
\ No newline at end of file
diff --git a/commitlint.config.js b/commitlint.config.js
new file mode 100644
index 0000000..3d88028
--- /dev/null
+++ b/commitlint.config.js
@@ -0,0 +1 @@
+module.exports = { extends: ['@commitlint/config-conventional'] };
\ No newline at end of file
diff --git a/foundry.toml b/foundry.toml
new file mode 100644
index 0000000..37cb0ae
--- /dev/null
+++ b/foundry.toml
@@ -0,0 +1,37 @@
+[fmt]
+line_length = 120
+tab_width = 2
+bracket_spacing = false
+int_types = 'long'
+quote_style = 'single'
+number_underscore = 'thousands'
+multiline_func_header = 'params_first'
+sort_imports = true
+
+[profile.default]
+solc_version = '0.8.25'
+evm_version = 'Cancun'
+libs = ['node_modules', 'lib']
+optimizer_runs = 10_000
+
+[profile.optimized]
+via_ir = true
+out = 'out-via-ir'
+
+[profile.test]
+via_ir = true
+out = 'out-via-ir'
+
+[profile.docs]
+src = 'src/interfaces/'
+
+[fuzz]
+runs = 1000
+
+[rpc_endpoints]
+optimism = "${OPTIMISM_SEPOLIA_RPC}"
+unichain = "${UNICHAIN_SEPOLIA_RPC}"
+
+[etherscan]
+optimism = { chain="11155420", key = "${OPTIMISM_EXPLORER_API_KEY}", url = "${OPTIMISM_EXPLORER_API}" }
+
diff --git a/natspec-smells.config.js b/natspec-smells.config.js
new file mode 100644
index 0000000..c918362
--- /dev/null
+++ b/natspec-smells.config.js
@@ -0,0 +1,9 @@
+/**
+ * List of supported options: https://github.com/defi-wonderland/natspec-smells?tab=readme-ov-file#options
+ */
+
+/** @type {import('@defi-wonderland/natspec-smells').Config} */
+module.exports = {
+ include: 'src/**/*.sol',
+ exclude: '(test|scripts)/**/*.sol',
+};
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..366704e
--- /dev/null
+++ b/package.json
@@ -0,0 +1,52 @@
+{
+ "name": "interop-sepolia",
+ "version": "1.0.0",
+ "description": "OP Interoperability mockup for Sepolia",
+ "homepage": "https://github.com/defi-wonderland/interop-sepolia#readme",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/defi-wonderland/interop-sepolia.git"
+ },
+ "license": "MIT",
+ "author": "Wonderland",
+ "scripts": {
+ "build": "forge build",
+ "build:optimized": "FOUNDRY_PROFILE=optimized forge build",
+ "coverage": "forge coverage --report summary --report lcov --match-path 'test/unit/*'",
+ "deploy:mode:sepolia": "bash -c 'source .env && forge clean && forge script script/Deploy.s.sol:Deploy --rpc-url $MODE_SEPOLIA_RPC --broadcast --verify --verifier blockscout --verifier-url $MODE_EXPLORER_API -vvvvv'",
+ "deploy:mode:sepolia:test": "bash -c 'source .env && forge script script/Deploy.s.sol:Deploy --rpc-url $MODE_SEPOLIA_RPC -vvvvv'",
+ "deploy:optimism:sepolia": "bash -c 'source .env && forge clean && forge script script/Deploy.s.sol:Deploy --rpc-url $OPTIMISM_SEPOLIA_RPC --broadcast --verify --chain optimism -vvvvv'",
+ "deploy:optimism:sepolia:test": "bash -c 'source .env && forge script script/Deploy.s.sol:Deploy --rpc-url $OPTIMISM_SEPOLIA_RPC -vvvvv'",
+ "deploy:unichain:sepolia": "bash -c 'source .env && forge clean && forge script script/Deploy.s.sol:Deploy --rpc-url $UNICHAIN_SEPOLIA_RPC --broadcast --verify --verifier blockscout --verifier-url $UNICHAIN_EXPLORER_API -vvvvv'",
+ "deploy:unichain:sepolia:test": "bash -c 'source .env && forge script script/Deploy.s.sol:Deploy --rpc-url $UNICHAIN_SEPOLIA_RPC -vvvvv'",
+ "lint:check": "yarn lint:sol && forge fmt --check",
+ "lint:fix": "sort-package-json && forge fmt && yarn lint:sol --fix",
+ "lint:natspec": "npx @defi-wonderland/natspec-smells --config natspec-smells.config.js",
+ "lint:sol": "solhint 'src/**/*.sol' 'script/**/*.sol' 'test/**/*.sol'",
+ "prepare": "husky",
+ "test": "forge test -vvv",
+ "test:integration": "forge test --match-contract Integration -vvv",
+ "test:unit": "forge test --match-contract Unit -vvv",
+ "test:unit:deep": "FOUNDRY_FUZZ_RUNS=5000 yarn test:unit"
+ },
+ "lint-staged": {
+ "*.{js,css,md,ts,sol}": "forge fmt",
+ "(src|test|script)/**/*.sol": "yarn lint:sol",
+ "package.json": "sort-package-json"
+ },
+ "dependencies": {
+ "@openzeppelin/contracts": "5.0.2",
+ "@openzeppelin/contracts-upgradeable": "5.0.2",
+ "optimism": "ethereum-optimism/optimism#develop"
+ },
+ "devDependencies": {
+ "@commitlint/cli": "19.3.0",
+ "@commitlint/config-conventional": "19.2.2",
+ "@defi-wonderland/natspec-smells": "1.1.3",
+ "forge-std": "github:foundry-rs/forge-std#1.9.2",
+ "husky": ">=9",
+ "lint-staged": ">=10",
+ "solhint-community": "4.0.0",
+ "sort-package-json": "2.10.0"
+ }
+}
\ No newline at end of file
diff --git a/remappings.txt b/remappings.txt
new file mode 100644
index 0000000..04ae658
--- /dev/null
+++ b/remappings.txt
@@ -0,0 +1,6 @@
+contracts/=src/contracts
+interfaces/=src/interfaces
+forge-std/=node_modules/forge-std/src
+optimism/=node_modules/optimism/packages/contracts-bedrock/
+@openzeppelin/=node_modules/@openzeppelin
+src/libraries/=node_modules/optimism/packages/contracts-bedrock/src/libraries/
\ No newline at end of file
diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol
new file mode 100644
index 0000000..d664f40
--- /dev/null
+++ b/script/Deploy.s.sol
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.25;
+
+import {ERC1967Proxy} from '@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol';
+import {CrossL2Inbox} from 'contracts/CrossL2Inbox.sol';
+import {L2ToL2CrossDomainMessenger} from 'contracts/L2ToL2CrossDomainMessenger.sol';
+import {Script} from 'forge-std/Script.sol';
+
+interface ICreate2Deployer {
+ /**
+ * @dev Deploys a contract using `CREATE2`. The address where the
+ * contract will be deployed can be known in advance via {computeAddress}.
+ *
+ * The bytecode for a contract can be obtained from Solidity with
+ * `type(contractName).creationCode`.
+ *
+ * Requirements:
+ * - `bytecode` must not be empty.
+ * - `salt` must have not been used for `bytecode` already.
+ * - the factory must have a balance of at least `value`.
+ * - if `value` is non-zero, `bytecode` must have a `payable` constructor.
+ */
+ function deploy(uint256 value, bytes32 salt, bytes memory code) external;
+
+ /**
+ * @dev Returns the address where a contract will be stored if deployed via {deploy}.
+ * Any change in the `bytecodeHash` or `salt` will result in a new destination address.
+ */
+ function computeAddress(bytes32 salt, bytes32 codeHash) external view returns (address);
+}
+
+/// @title Deploy
+/// @notice This script deploys the CrossL2Inbox and L2ToL2CrossDomainMessenger contracts
+/// with the same address on every chain using Create2Deployer. Since the owner
+/// is part of the creation code, it should be the same on every chain.
+contract Deploy is Script {
+ ICreate2Deployer public constant CREATE_2_DEPLOYER = ICreate2Deployer(0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2);
+
+ function run() external returns (address _inboxProxy, address _messengerProxy) {
+ bytes32 _salt = keccak256(abi.encodePacked(vm.envString('CREATE2_SALT')));
+ address _owner = vm.envAddress('OWNER_ADDR');
+
+ vm.startBroadcast(vm.envUint('DEPLOYER_PK'));
+
+ // Creation code of CrossL2Inbox Implementation
+ bytes memory _inboxImplementationCreationCode = type(CrossL2Inbox).creationCode;
+
+ // Deploy CrossL2Inbox Implementation using Create2Deployer
+ address _inboxImplementation = _deploy(_salt, _inboxImplementationCreationCode);
+
+ // Creation code of CrossL2Inbox Proxy
+ bytes memory _inboxCreationCode = bytes.concat(
+ type(ERC1967Proxy).creationCode,
+ abi.encode(_inboxImplementation, abi.encodeWithSelector(CrossL2Inbox.initialize.selector, _owner))
+ );
+
+ // Deploy Proxy and Initialize
+ _inboxProxy = _deploy(_salt, _inboxCreationCode);
+
+ // Creation code of L2ToL2CrossDomainMessenger Implementation
+ bytes memory _messengerImplementationCreationCode =
+ bytes.concat(type(L2ToL2CrossDomainMessenger).creationCode, abi.encode(_inboxProxy));
+
+ // Deploy L2ToL2CrossDomainMessenger Implementation using Create2Deployer
+ address _messengerImplementation = _deploy(_salt, _messengerImplementationCreationCode);
+
+ // Creation code of L2ToL2CrossDomainMessenger Proxy
+ bytes memory _messengerCreationCode = bytes.concat(
+ type(ERC1967Proxy).creationCode,
+ abi.encode(_messengerImplementation, abi.encodeWithSelector(CrossL2Inbox.initialize.selector, _owner))
+ );
+
+ // Deploy Proxy and Initialize
+ _messengerProxy = _deploy(_salt, _messengerCreationCode);
+
+ vm.stopBroadcast();
+ }
+
+ function _deploy(bytes32 _salt, bytes memory _creationCode) internal returns (address _address) {
+ // Pre compute address
+ _address = CREATE_2_DEPLOYER.computeAddress(_salt, keccak256(_creationCode));
+
+ // Deploy
+ CREATE_2_DEPLOYER.deploy(0, _salt, _creationCode);
+ }
+}
diff --git a/src/contracts/CrossL2Inbox.sol b/src/contracts/CrossL2Inbox.sol
new file mode 100644
index 0000000..93bfd2c
--- /dev/null
+++ b/src/contracts/CrossL2Inbox.sol
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.25;
+
+import {OwnableUpgradeable} from '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
+import {UUPSUpgradeable} from '@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol';
+import {ISemver} from 'optimism/src/universal/interfaces/ISemver.sol';
+
+/// @notice The struct for a pointer to a message payload in a remote (or local) chain.
+/// @param origin The address of the chain where the message originated.
+/// @param blockNumber The block number where the message was emitted.
+/// @param logIndex The log index of the message in the block.
+/// @param timestamp The timestamp of the block where the message was emitted.
+/// @param chainId The chain id where the message originated.
+struct Identifier {
+ address origin;
+ uint256 blockNumber;
+ uint256 logIndex;
+ uint256 timestamp;
+ uint256 chainId;
+}
+
+/// @notice Thrown when trying to execute a function that requires the caller to be a valid relayer.
+error InvalidRelayer();
+
+/// @notice Thrown when trying to execute a cross chain message with an invalid hash.
+error InvalidHash();
+
+/// @custom:proxied true
+/// @title CrossL2Inbox
+/// @notice The CrossL2Inbox is responsible for executing a cross chain message on the destination
+/// chain. It is permissionless to validate a cross chain message on behalf of any user.
+contract CrossL2Inbox is ISemver, UUPSUpgradeable, OwnableUpgradeable {
+ /// @notice Emitted when a cross chain message is being executed.
+ /// @param msgHash Hash of message payload being executed.
+ /// @param id Encoded Identifier of the message.
+ event ExecutingMessage(bytes32 indexed msgHash, Identifier id);
+
+ /// @notice Emitted when a relayer's status is updated.
+ /// @param account The address of the relayer whose status is being updated.
+ /// @param isRelayer A boolean indicating whether the relayer is enabled (true) or disabled (false).
+ event UpdatedRelayer(address indexed account, bool isRelayer);
+
+ /// @notice Emitted when a new hash is registered as valid for cross chain messages.
+ /// @param hash The hash of the cross chain message that has been registered.
+ event RegisteredHash(bytes32 indexed hash);
+
+ /// @notice Semantic version.
+ /// @custom:semver 1.0.0-mock
+ string public constant version = '1.0.0-mock';
+
+ /// @notice Authorized relayers that can execute cross chain messages.
+ mapping(address _sender => bool) public validRelayers;
+
+ /// @notice Authorized hashes of cross chain messages that can be executed.
+ /// @dev Hashes are generated using:
+ /// keccak256(abi.encode(identifier, keccak256(payload)))
+ /// where payload is the encoded chainId, target contract, nonce, sender,
+ /// and the calldata sent to the target contract.
+ mapping(bytes32 _hash => bool) public validHashes;
+
+ /// @notice Initializes the contract disabling the initializers.
+ constructor() OwnableUpgradeable() {
+ _disableInitializers();
+ }
+
+ /// @notice Initialize the contract
+ /// @param _owner The owner of the contract
+ function initialize(
+ address _owner
+ ) external initializer {
+ __Ownable_init(_owner);
+ }
+
+ /// @notice Validates a cross chain message on the destination chain
+ /// and emits an ExecutingMessage event. This function is useful
+ /// for applications that understand the schema of the _message payload and want to
+ /// process it in a custom way.
+ /// @param _id Identifier of the message.
+ /// @param _msgHash Hash of the message payload to call target with.
+ function validateMessage(Identifier calldata _id, bytes32 _msgHash) external {
+ // Check the Id and Message hash
+ _checkHash(_id, _msgHash);
+
+ emit ExecutingMessage(_msgHash, _id);
+ }
+
+ /// @notice Enables a relayer to operate with the contract.
+ /// @param _relayer The address of the relayer.
+ function enableRelayer(
+ address _relayer
+ ) external onlyOwner {
+ validRelayers[_relayer] = true;
+
+ emit UpdatedRelayer(_relayer, true);
+ }
+
+ /// @notice Disables a relayer from operating with the contract.
+ /// @param _relayer The address of the relayer.
+ function disableRelayer(
+ address _relayer
+ ) external onlyOwner {
+ validRelayers[_relayer] = false;
+
+ emit UpdatedRelayer(_relayer, false);
+ }
+
+ /// @notice Registers a valid hash for a cross chain message.
+ /// @dev Hashes are generated using:
+ /// keccak256(abi.encode(identifier, keccak256(payload)))
+ /// where payload is the encoded chainId, target contract, nonce, sender,
+ /// and the calldata sent to the target contract.
+ /// @param _hash Hash of the cross chain message.
+ function registerValidHash(
+ bytes32 _hash
+ ) external {
+ if (!validRelayers[msg.sender]) revert InvalidRelayer();
+ validHashes[_hash] = true;
+
+ emit RegisteredHash(_hash);
+ }
+
+ /// @notice Authorizes an upgrade to a new implementation.
+ /// @param _newImplementation Address of the new implementation (not used).
+ function _authorizeUpgrade(
+ address _newImplementation
+ ) internal override onlyOwner {}
+
+ /// @notice Validates that for a given cross chain message identifier and message hash
+ /// the message can be executed on the destination chain.
+ /// @param _id Identifier of the message.
+ /// @param _msgHash Hash of the message payload to call target with.
+ function _checkHash(Identifier calldata _id, bytes32 _msgHash) internal view {
+ bytes32 _hash = keccak256(abi.encode(_id, _msgHash));
+ if (!validHashes[_hash]) revert InvalidHash();
+ }
+}
diff --git a/src/contracts/L2ToL2CrossDomainMessenger.sol b/src/contracts/L2ToL2CrossDomainMessenger.sol
new file mode 100644
index 0000000..0bf6ae9
--- /dev/null
+++ b/src/contracts/L2ToL2CrossDomainMessenger.sol
@@ -0,0 +1,269 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.25;
+
+import {OwnableUpgradeable} from '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
+import {UUPSUpgradeable} from '@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol';
+import {CrossL2Inbox, Identifier} from 'contracts/CrossL2Inbox.sol';
+import {Encoding} from 'optimism/src/libraries/Encoding.sol';
+import {Hashing} from 'optimism/src/libraries/Hashing.sol';
+import {SafeCall} from 'optimism/src/libraries/SafeCall.sol';
+import {TransientReentrancyAware} from 'optimism/src/libraries/TransientContext.sol';
+import {ISemver} from 'optimism/src/universal/interfaces/ISemver.sol';
+
+/// @notice Thrown when attempting to relay a message where payload origin is not L2ToL2CrossDomainMessenger.
+error IdOriginNotL2ToL2CrossDomainMessenger();
+
+/// @notice Thrown when the payload provided to the relay is not a SentMessage event.
+error EventPayloadNotSentMessage();
+
+/// @notice Thrown when attempting to send a message to the chain that the message is being sent from.
+error MessageDestinationSameChain();
+
+/// @notice Thrown when attempting to relay a message whose destination chain is not the chain relaying it.
+error MessageDestinationNotRelayChain();
+
+/// @notice Thrown when attempting to relay a message whose target is CrossL2Inbox.
+error MessageTargetCrossL2Inbox();
+
+/// @notice Thrown when attempting to relay a message whose target is L2ToL2CrossDomainMessenger.
+error MessageTargetL2ToL2CrossDomainMessenger();
+
+/// @notice Thrown when attempting to relay a message that has already been relayed.
+error MessageAlreadyRelayed();
+
+/// @notice Thrown when a call to the target contract during message relay fails.
+error TargetCallFailed();
+
+/// @custom:proxied true
+/// @title L2ToL2CrossDomainMessenger
+/// @notice The L2ToL2CrossDomainMessenger is a higher level abstraction on top of the CrossL2Inbox that provides
+/// features necessary for secure transfers ERC20 tokens between L2 chains. Messages sent through the
+/// L2ToL2CrossDomainMessenger on the source chain receive both replay protection as well as domain binding.
+contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware, OwnableUpgradeable, UUPSUpgradeable {
+ /// @notice Emitted whenever a message is sent to a destination
+ /// @param destination Chain ID of the destination chain.
+ /// @param target Target contract or wallet address.
+ /// @param messageNonce Nonce associated with the messsage sent
+ /// @param sender Address initiating this message call
+ /// @param message Message payload to call target with.
+ event SentMessage(
+ uint256 indexed destination, address indexed target, uint256 indexed messageNonce, address sender, bytes message
+ );
+
+ /// @notice Emitted whenever a message is successfully relayed on this chain.
+ /// @param source Chain ID of the source chain.
+ /// @param messageNonce Nonce associated with the messsage sent
+ /// @param messageHash Hash of the message that was relayed.
+ event RelayedMessage(uint256 indexed source, uint256 indexed messageNonce, bytes32 indexed messageHash);
+
+ /// @notice Storage slot for the sender of the current cross domain message.
+ /// Equal to bytes32(uint256(keccak256("l2tol2crossdomainmessenger.sender")) - 1)
+ bytes32 internal constant CROSS_DOMAIN_MESSAGE_SENDER_SLOT =
+ 0xb83444d07072b122e2e72a669ce32857d892345c19856f4e7142d06a167ab3f3;
+
+ /// @notice Storage slot for the source of the current cross domain message.
+ /// Equal to bytes32(uint256(keccak256("l2tol2crossdomainmessenger.source")) - 1)
+ bytes32 internal constant CROSS_DOMAIN_MESSAGE_SOURCE_SLOT =
+ 0x711dfa3259c842fffc17d6e1f1e0fc5927756133a2345ca56b4cb8178589fee7;
+
+ /// @notice Event selector for the SentMessage event. Will be removed in favor of reading
+ // the `selector` property directly once crytic/slithe/#2566 is fixed.
+ bytes32 internal constant SENT_MESSAGE_EVENT_SELECTOR =
+ 0x382409ac69001e11931a28435afef442cbfd20d9891907e8fa373ba7d351f320;
+
+ /// @notice Current message version identifier.
+ uint16 public constant messageVersion = uint16(0);
+
+ /// @notice Semantic version.
+ /// @custom:semver 1.0.0-mock
+ string public constant version = '1.0.0-mock';
+
+ /// @notice Address of the CrossL2Inbox for message validation.
+ address public immutable CROSS_L2_INBOX;
+
+ /// @notice Mapping of message hashes to boolean receipt values. Note that a message will only be present in this
+ /// mapping if it has successfully been relayed on this chain, and can therefore not be relayed again.
+ mapping(bytes32 _messageHash => bool) public successfulMessages;
+
+ /// @notice Nonce for the next message to be sent, without the message version applied. Use the messageNonce getter,
+ /// which will insert the message version into the nonce to give you the actual nonce to be used for the
+ /// message.
+ uint240 internal msgNonce;
+
+ /// @notice Initializes the contract disabling the initializers and setting the CrossL2Inbox address.
+ /// @param _crossL2Inbox Address of the CrossL2Inbox contract.
+ constructor(
+ address _crossL2Inbox
+ ) {
+ CROSS_L2_INBOX = _crossL2Inbox;
+ _disableInitializers();
+ }
+
+ /// @notice Initialize the contract
+ /// @param _owner The owner of the contract
+ function initialize(
+ address _owner
+ ) external initializer {
+ __Ownable_init(_owner);
+ }
+
+ /// @notice Retrieves the sender of the current cross domain message. If not entered, reverts.
+ /// @return sender_ Address of the sender of the current cross domain message.
+ function crossDomainMessageSender() external view onlyEntered returns (address sender_) {
+ assembly {
+ sender_ := tload(CROSS_DOMAIN_MESSAGE_SENDER_SLOT)
+ }
+ }
+
+ /// @notice Retrieves the source of the current cross domain message. If not entered, reverts.
+ /// @return source_ Chain ID of the source of the current cross domain message.
+ function crossDomainMessageSource() external view onlyEntered returns (uint256 source_) {
+ assembly {
+ source_ := tload(CROSS_DOMAIN_MESSAGE_SOURCE_SLOT)
+ }
+ }
+
+ /// @notice Retrieves the context of the current cross domain message. If not entered, reverts.
+ /// @return sender_ Address of the sender of the current cross domain message.
+ /// @return source_ Chain ID of the source of the current cross domain message.
+ function crossDomainMessageContext() external view onlyEntered returns (address sender_, uint256 source_) {
+ assembly {
+ sender_ := tload(CROSS_DOMAIN_MESSAGE_SENDER_SLOT)
+ source_ := tload(CROSS_DOMAIN_MESSAGE_SOURCE_SLOT)
+ }
+ }
+
+ /// @notice Sends a message to some target address on a destination chain. Note that if the call always reverts,
+ /// then the message will be unrelayable and any ETH sent will be permanently locked. The same will occur
+ /// if the target on the other chain is considered unsafe (see the _isUnsafeTarget() function).
+ /// @param _destination Chain ID of the destination chain.
+ /// @param _target Target contract or wallet address.
+ /// @param _message Message payload to call target with.
+ /// @return hash_ The hash of the message being sent, used to track whether the message has successfully been relayed.
+ function sendMessage(uint256 _destination, address _target, bytes calldata _message) external returns (bytes32 hash_) {
+ if (_destination == block.chainid) revert MessageDestinationSameChain();
+ if (_target == CROSS_L2_INBOX) revert MessageTargetCrossL2Inbox();
+ if (_target == address(this)) revert MessageTargetL2ToL2CrossDomainMessenger();
+
+ uint256 nonce = messageNonce();
+ emit SentMessage(_destination, _target, nonce, msg.sender, _message);
+
+ msgNonce++;
+
+ hash_ = Hashing.hashL2toL2CrossDomainMessage({
+ _destination: _destination,
+ _source: block.chainid,
+ _nonce: nonce,
+ _sender: msg.sender,
+ _target: _target,
+ _message: _message
+ });
+ }
+
+ /// @notice Relays a message that was sent by the other L2ToL2CrossDomainMessenger contract. Can only be executed
+ /// via cross chain call from the other messenger OR if the message was already received once and is
+ /// currently being replayed.
+ /// @param _id Identifier of the SentMessage event to be relayed
+ /// @param _sentMessage Message payload of the `SentMessage` event
+ function relayMessage(Identifier calldata _id, bytes calldata _sentMessage) external payable nonReentrant {
+ // Ensure the log came from the messenger. Since the log origin is the CDM, there isn't a scenario where
+ // this can be invoked from the CrossL2Inbox as the SentMessage log is not calldata for this function
+ if (_id.origin != address(this)) {
+ revert IdOriginNotL2ToL2CrossDomainMessenger();
+ }
+
+ // Signal that this is a cross chain call that needs to have the identifier validated
+ CrossL2Inbox(CROSS_L2_INBOX).validateMessage(_id, keccak256(_sentMessage));
+
+ // Decode the payload
+ (uint256 destination, address target, uint256 nonce, address sender, bytes memory message) =
+ _decodeSentMessagePayload(_sentMessage);
+
+ // Assert invariants on the message
+ if (destination != block.chainid) revert MessageDestinationNotRelayChain();
+ if (target == CROSS_L2_INBOX) revert MessageTargetCrossL2Inbox();
+ if (target == address(this)) revert MessageTargetL2ToL2CrossDomainMessenger();
+
+ uint256 source = _id.chainId;
+ bytes32 messageHash = Hashing.hashL2toL2CrossDomainMessage({
+ _destination: destination,
+ _source: source,
+ _nonce: nonce,
+ _sender: sender,
+ _target: target,
+ _message: message
+ });
+
+ if (successfulMessages[messageHash]) {
+ revert MessageAlreadyRelayed();
+ }
+
+ _storeMessageMetadata(source, sender);
+
+ bool success = SafeCall.call(target, msg.value, message);
+
+ if (!success) {
+ revert TargetCallFailed();
+ }
+
+ successfulMessages[messageHash] = true;
+ emit RelayedMessage(source, nonce, messageHash);
+
+ _storeMessageMetadata(0, address(0));
+ }
+
+ /// @notice Retrieves the next message nonce. Message version will be added to the upper two bytes of the message
+ /// nonce. Message version allows us to treat messages as having different structures.
+ /// @return Nonce of the next message to be sent, with added message version.
+ function messageNonce() public view returns (uint256) {
+ return Encoding.encodeVersionedNonce(msgNonce, messageVersion);
+ }
+
+ /// @notice Stores message data such as sender and source in transient storage.
+ /// @param _source Chain ID of the source chain.
+ /// @param _sender Address of the sender of the message.
+ function _storeMessageMetadata(uint256 _source, address _sender) internal {
+ assembly {
+ tstore(CROSS_DOMAIN_MESSAGE_SENDER_SLOT, _sender)
+ tstore(CROSS_DOMAIN_MESSAGE_SOURCE_SLOT, _source)
+ }
+ }
+
+ /// @notice Decodes the payload of a SentMessage event.
+ /// The payload must be structured as follows:
+ /// - First 32 bytes: Event selector (must match SENT_MESSAGE_EVENT_SELECTOR).
+ /// - Next 96 bytes: Encoded data containing:
+ /// - 32 bytes: Destination chain ID (uint256).
+ /// - 32 bytes: Target contract address (address).
+ /// - 32 bytes: Nonce (uint256).
+ /// - Remaining bytes: Sender address (address) and message payload (bytes).
+ /// @param _payload The encoded payload of the SentMessage event.
+ /// @return destination_ Chain ID of the destination.
+ /// @return target_ Target contract address.
+ /// @return nonce_ Nonce associated with the message.
+ /// @return sender_ Address of the sender of the message.
+ /// @return message_ The message payload to be sent to the target.
+ function _decodeSentMessagePayload(
+ bytes calldata _payload
+ )
+ internal
+ pure
+ returns (uint256 destination_, address target_, uint256 nonce_, address sender_, bytes memory message_)
+ {
+ // Validate Selector (also reverts if LOG0 with no topics)
+ bytes32 selector = abi.decode(_payload[:32], (bytes32));
+ if (selector != SENT_MESSAGE_EVENT_SELECTOR) revert EventPayloadNotSentMessage();
+
+ // Topics
+ (destination_, target_, nonce_) = abi.decode(_payload[32:128], (uint256, address, uint256));
+
+ // Data
+ (sender_, message_) = abi.decode(_payload[128:], (address, bytes));
+ }
+
+ /// @notice Authorizes an upgrade to a new implementation.
+ /// @param _newImplementation Address of the new implementation (not used).
+ function _authorizeUpgrade(
+ address _newImplementation
+ ) internal override onlyOwner {}
+}
diff --git a/test/integration/Deploy.t.sol b/test/integration/Deploy.t.sol
new file mode 100644
index 0000000..f0633b4
--- /dev/null
+++ b/test/integration/Deploy.t.sol
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.25;
+
+import {OwnableUpgradeable} from '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
+import {CrossL2Inbox} from 'contracts/CrossL2Inbox.sol';
+import {L2ToL2CrossDomainMessenger} from 'contracts/L2ToL2CrossDomainMessenger.sol';
+import {Test} from 'forge-std/Test.sol';
+import {Deploy} from 'script/Deploy.s.sol';
+
+contract DeployTest is Test {
+ CrossL2Inbox internal _unichainInbox;
+ L2ToL2CrossDomainMessenger internal _unichainL2ToL2Cdm;
+ CrossL2Inbox internal _optimismInbox;
+ L2ToL2CrossDomainMessenger internal _optimismL2ToL2Cdm;
+ address internal _deployer = vm.addr(vm.envUint('DEPLOYER_PK'));
+ // We need to use the same owner address used in the deploy script to test proxy upgrades.
+ address internal _owner = vm.envAddress('OWNER_ADDR');
+ uint256 internal _optimism;
+ uint256 internal _unichain;
+
+ /// @dev Since the deploy shuould be nonce agnostic, we need to set the nonce to a random value.
+ function setUp() external {
+ _optimism = vm.createFork(vm.rpcUrl('optimism'), 19_032_508);
+ _unichain = vm.createFork(vm.rpcUrl('unichain'), 3_015_172);
+
+ vm.selectFork(_optimism);
+ // Randomize deployer nonce
+ vm.setNonce(_deployer, uint64(block.prevrandao));
+ Deploy _deploy = new Deploy();
+ (address _inboxAddress, address _cdmAddress) = _deploy.run();
+
+ _optimismInbox = CrossL2Inbox(_inboxAddress);
+ _optimismL2ToL2Cdm = L2ToL2CrossDomainMessenger(_cdmAddress);
+
+ vm.selectFork(_unichain);
+ // Randomize deployer nonce
+ vm.setNonce(_deployer, uint64(block.prevrandao));
+ _deploy = new Deploy();
+ (_inboxAddress, _cdmAddress) = _deploy.run();
+
+ _unichainInbox = CrossL2Inbox(_inboxAddress);
+ _unichainL2ToL2Cdm = L2ToL2CrossDomainMessenger(_cdmAddress);
+ }
+
+ /// @notice Test that contract is correctly initialized.
+ function test_inboxInitialize() external view {
+ assertEq(address(_unichainInbox), address(_optimismInbox), 'Different inbox addresses');
+ assertEq(_unichainInbox.owner(), _owner, 'Incorrect owner on unichain');
+ assertEq(_optimismInbox.owner(), _owner, 'Incorrect owner on optimism');
+ }
+
+ /// @notice Test that contract is correctly initialized.
+ function test_cdmInitialize() external view {
+ assertEq(address(_unichainL2ToL2Cdm), address(_optimismL2ToL2Cdm), 'Different inbox addresses');
+ assertEq(_unichainInbox.owner(), _owner, 'Incorrect owner on unichain');
+ assertEq(_optimismInbox.owner(), _owner, 'Incorrect owner on optimism');
+ assertEq(_unichainL2ToL2Cdm.CROSS_L2_INBOX(), address(_unichainInbox), 'Incorrect inbox address');
+ assertEq(_optimismL2ToL2Cdm.CROSS_L2_INBOX(), address(_optimismInbox), 'Incorrect inbox address');
+ }
+
+ /// @notice Test that the owner can upgrade the proxy.
+ function test_ownerCanUpgradeProxy() external {
+ vm.startPrank(_owner);
+
+ vm.selectFork(_optimism);
+ _optimismInbox.upgradeToAndCall(address(new CrossL2Inbox()), bytes(''));
+ _optimismL2ToL2Cdm.upgradeToAndCall(address(new L2ToL2CrossDomainMessenger(address(1))), bytes(''));
+
+ vm.selectFork(_unichain);
+ _unichainInbox.upgradeToAndCall(address(new CrossL2Inbox()), bytes(''));
+ _unichainL2ToL2Cdm.upgradeToAndCall(address(new L2ToL2CrossDomainMessenger(address(1))), bytes(''));
+ }
+
+ /// @notice Test that the `upgradeToAndCall` function reverts when the caller is not the owner.
+ /// @dev Was getting an error with vm.expectRevert so I used the following code to test the reverts.
+ function test_notOwnerCanNotUpgradeProxy() external {
+ address _unauthorizedCaller = makeAddr('Unauthorized Caller');
+ vm.startPrank(_unauthorizedCaller);
+
+ vm.selectFork(_optimism);
+ // it reverts
+ (bool _success, bytes memory _returnData) = address(_optimismInbox).call(
+ abi.encodeWithSignature('upgradeToAndCall(address,bytes)', address(new CrossL2Inbox()), bytes(''))
+ );
+ assertEq(_success, false, 'Upgrade to and call on inbox not failed');
+ assertEq(
+ _returnData,
+ abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, _unauthorizedCaller),
+ 'Incorrect revert message'
+ );
+
+ // it reverts
+ (_success, _returnData) = address(_optimismL2ToL2Cdm).call(
+ abi.encodeWithSignature(
+ 'upgradeToAndCall(address,bytes)', address(new L2ToL2CrossDomainMessenger(address(1))), bytes('')
+ )
+ );
+ assertEq(_success, false, 'Upgrade to and call on messenger not failed');
+ assertEq(
+ _returnData,
+ abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, _unauthorizedCaller),
+ 'Incorrect revert message'
+ );
+
+ vm.selectFork(_unichain);
+ // it reverts
+ (_success, _returnData) = address(_optimismInbox).call(
+ abi.encodeWithSignature('upgradeToAndCall(address,bytes)', address(new CrossL2Inbox()), bytes(''))
+ );
+ assertEq(_success, false, 'Upgrade to and call on inbox not failed');
+ assertEq(
+ _returnData,
+ abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, _unauthorizedCaller),
+ 'Incorrect revert message'
+ );
+
+ (_success, _returnData) = address(_optimismL2ToL2Cdm).call(
+ abi.encodeWithSignature(
+ 'upgradeToAndCall(address,bytes)', address(new L2ToL2CrossDomainMessenger(address(1))), bytes('')
+ )
+ );
+ assertEq(_success, false, 'Upgrade to and call on messenger not failed');
+ assertEq(
+ _returnData,
+ abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, _unauthorizedCaller),
+ 'Incorrect revert message'
+ );
+ }
+}
diff --git a/test/integration/IntegrationTest.t.sol b/test/integration/IntegrationTest.t.sol
new file mode 100644
index 0000000..3ad5e49
--- /dev/null
+++ b/test/integration/IntegrationTest.t.sol
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.25;
+
+import {Helpers} from '../utils/Helpers.sol';
+import {CrossL2Inbox, Identifier, InvalidHash} from 'contracts/CrossL2Inbox.sol';
+import {L2ToL2CrossDomainMessenger, TargetCallFailed} from 'contracts/L2ToL2CrossDomainMessenger.sol';
+import {Test} from 'forge-std/Test.sol';
+
+/// @notice A simple contract to act as target for the message in a different chain
+contract ForTestCounter {
+ uint256 internal _a;
+
+ function increaseCounter() external {
+ _a++;
+ }
+
+ function getCounter() external view returns (uint256 counter_) {
+ counter_ = _a;
+ }
+}
+
+contract IntegrationTest is Test {
+ uint256 internal constant _NUM_MESSAGES = 50;
+
+ address internal immutable _RELAYER = makeAddr('Relayer');
+ address internal immutable _INBOX_OWNER = makeAddr('Owner');
+ address internal immutable _CROSS_L2_INBOX = makeAddr('CrossL2Inbox');
+ address internal immutable _L2_TO_L2_CROSS_DOMAIN_MESSENGER = makeAddr('L2ToL2CrossDomainMessenger');
+ address internal immutable _TARGET = makeAddr('TargetContract');
+
+ uint256 internal _unichain;
+ uint256 internal _optimism;
+ uint256 internal _unichainId;
+ uint256 internal _optimismId;
+
+ function setUp() external {
+ _unichain = vm.createFork(vm.rpcUrl('unichain'), 3_015_172);
+ _optimism = vm.createFork(vm.rpcUrl('optimism'), 19_032_508);
+
+ bytes memory _crossL2Inbox = address(new CrossL2Inbox()).code;
+ bytes memory _l2ToL2Cdm = address(new L2ToL2CrossDomainMessenger(_CROSS_L2_INBOX)).code;
+ bytes memory _target = address(new ForTestCounter()).code;
+
+ // Unichain setup
+ vm.selectFork(_unichain);
+ _unichainId = block.chainid;
+ vm.etch(_CROSS_L2_INBOX, _crossL2Inbox);
+ vm.etch(_L2_TO_L2_CROSS_DOMAIN_MESSENGER, _l2ToL2Cdm);
+ vm.etch(_TARGET, _target);
+
+ CrossL2Inbox(_CROSS_L2_INBOX).initialize(_INBOX_OWNER);
+ L2ToL2CrossDomainMessenger(_L2_TO_L2_CROSS_DOMAIN_MESSENGER).initialize(_INBOX_OWNER);
+
+ vm.prank(_INBOX_OWNER);
+ CrossL2Inbox(_CROSS_L2_INBOX).enableRelayer(_RELAYER);
+
+ // Optimism setup
+ vm.selectFork(_optimism);
+ _optimismId = block.chainid;
+ vm.etch(_CROSS_L2_INBOX, _crossL2Inbox);
+ vm.etch(_L2_TO_L2_CROSS_DOMAIN_MESSENGER, _l2ToL2Cdm);
+ vm.etch(_TARGET, _target);
+
+ CrossL2Inbox(_CROSS_L2_INBOX).initialize(_INBOX_OWNER);
+ L2ToL2CrossDomainMessenger(_L2_TO_L2_CROSS_DOMAIN_MESSENGER).initialize(_INBOX_OWNER);
+
+ vm.prank(_INBOX_OWNER);
+ CrossL2Inbox(_CROSS_L2_INBOX).enableRelayer(_RELAYER);
+ }
+
+ /// @notice Test relaying messages when the hash is valid but the target call fails.
+ function test_Integration_invalidTarget() external {
+ bytes memory _message = abi.encodeWithSignature('notAFunction()');
+
+ vm.selectFork(_optimism);
+ uint256 _nonce = L2ToL2CrossDomainMessenger(_L2_TO_L2_CROSS_DOMAIN_MESSENGER).messageNonce();
+ L2ToL2CrossDomainMessenger(_L2_TO_L2_CROSS_DOMAIN_MESSENGER).sendMessage(_unichainId, _TARGET, _message);
+
+ // Build Identifier
+ Identifier memory _identifier = Identifier({
+ origin: _L2_TO_L2_CROSS_DOMAIN_MESSENGER,
+ blockNumber: block.number,
+ logIndex: 0,
+ timestamp: block.timestamp,
+ chainId: _optimismId
+ });
+
+ bytes memory _payload = Helpers.encodePayload(_unichainId, _TARGET, _nonce, address(this), _message);
+ bytes32 _messageHash = keccak256(abi.encode(_identifier, keccak256(_payload)));
+
+ vm.selectFork(_unichain);
+
+ vm.prank(_RELAYER);
+ CrossL2Inbox(_CROSS_L2_INBOX).registerValidHash(_messageHash);
+
+ vm.expectRevert(TargetCallFailed.selector);
+ L2ToL2CrossDomainMessenger(_L2_TO_L2_CROSS_DOMAIN_MESSENGER).relayMessage(_identifier, _payload);
+ }
+
+ /// @notice Test relaying messages between chains when the hash is not registered.
+ function test_Integration_invalidHash() external {
+ bytes memory _message = abi.encodeCall(ForTestCounter.increaseCounter, ());
+
+ vm.selectFork(_optimism);
+ uint256 _nonce = L2ToL2CrossDomainMessenger(_L2_TO_L2_CROSS_DOMAIN_MESSENGER).messageNonce();
+ L2ToL2CrossDomainMessenger(_L2_TO_L2_CROSS_DOMAIN_MESSENGER).sendMessage(_unichainId, _TARGET, _message);
+
+ // Build Identifier
+ Identifier memory _identifier = Identifier({
+ origin: _L2_TO_L2_CROSS_DOMAIN_MESSENGER,
+ blockNumber: block.number,
+ logIndex: 0,
+ timestamp: block.timestamp,
+ chainId: _optimismId
+ });
+
+ bytes memory _payload = Helpers.encodePayload(_unichainId, _TARGET, _nonce, address(this), _message);
+
+ vm.selectFork(_unichain);
+
+ vm.expectRevert(InvalidHash.selector);
+ L2ToL2CrossDomainMessenger(_L2_TO_L2_CROSS_DOMAIN_MESSENGER).relayMessage(_identifier, _payload);
+
+ assertEq(ForTestCounter(_TARGET).getCounter(), 0, 'Counter should not have been increased');
+ }
+
+ /// @notice Test sending a message from optimism to unichain, the message has a target contract.
+ function test_Integration_sendInterchainMessage() external {
+ bytes memory _message = abi.encodeCall(ForTestCounter.increaseCounter, ());
+
+ vm.selectFork(_optimism);
+ uint256 _nonce = L2ToL2CrossDomainMessenger(_L2_TO_L2_CROSS_DOMAIN_MESSENGER).messageNonce();
+ L2ToL2CrossDomainMessenger(_L2_TO_L2_CROSS_DOMAIN_MESSENGER).sendMessage(_unichainId, _TARGET, _message);
+
+ // Build Identifier
+ Identifier memory _identifier = Identifier({
+ origin: _L2_TO_L2_CROSS_DOMAIN_MESSENGER,
+ blockNumber: block.number,
+ logIndex: 0,
+ timestamp: block.timestamp,
+ chainId: _optimismId
+ });
+
+ bytes memory _payload = Helpers.encodePayload(_unichainId, _TARGET, _nonce, address(this), _message);
+ bytes32 _messageHash = keccak256(abi.encode(_identifier, keccak256(_payload)));
+
+ vm.selectFork(_unichain);
+
+ vm.prank(_RELAYER);
+ CrossL2Inbox(_CROSS_L2_INBOX).registerValidHash(_messageHash);
+
+ L2ToL2CrossDomainMessenger(_L2_TO_L2_CROSS_DOMAIN_MESSENGER).relayMessage(_identifier, _payload);
+
+ assertEq(ForTestCounter(_TARGET).getCounter(), 1, 'Counter should have been increased');
+ }
+
+ function test_sendMultiInterchainMessages(
+ bool[_NUM_MESSAGES] memory _shouldMessageSucceed,
+ address[_NUM_MESSAGES] memory _accounts
+ ) external {
+ vm.selectFork(_optimism);
+
+ Identifier[_NUM_MESSAGES] memory _identifiers;
+ bytes[_NUM_MESSAGES] memory _payloads;
+ bytes32[_NUM_MESSAGES] memory _hashes;
+
+ // counter we can use to compare with _TARGET contract state
+ uint256 _successfulMessages;
+
+ for (uint256 _i = 0; _i < _NUM_MESSAGES; _i++) {
+ bytes memory _message = _shouldMessageSucceed[_i]
+ ? abi.encodeCall(ForTestCounter.increaseCounter, ())
+ : abi.encodeWithSignature('InexistentFunction()');
+
+ uint256 _nonce = L2ToL2CrossDomainMessenger(_L2_TO_L2_CROSS_DOMAIN_MESSENGER).messageNonce();
+ L2ToL2CrossDomainMessenger(_L2_TO_L2_CROSS_DOMAIN_MESSENGER).sendMessage(_unichainId, _TARGET, _message);
+
+ // Build Identifier
+ Identifier memory _identifier = Identifier({
+ origin: _L2_TO_L2_CROSS_DOMAIN_MESSENGER,
+ blockNumber: block.number,
+ logIndex: 0,
+ timestamp: block.timestamp,
+ chainId: _optimismId
+ });
+
+ _identifiers[_i] = _identifier;
+
+ bytes memory _payload = Helpers.encodePayload(_unichainId, _TARGET, _nonce, address(this), _message);
+ _payloads[_i] = _payload;
+ _hashes[_i] = keccak256(abi.encode(_identifier, keccak256(_payload)));
+
+ if (_shouldMessageSucceed[_i]) _successfulMessages++;
+ }
+
+ vm.selectFork(_unichain);
+ for (uint256 _i = 0; _i < _NUM_MESSAGES; _i++) {
+ vm.prank(_RELAYER);
+ CrossL2Inbox(_CROSS_L2_INBOX).registerValidHash(_hashes[_i]);
+
+ if (!_shouldMessageSucceed[_i]) vm.expectRevert();
+
+ vm.prank(_accounts[_i]);
+ L2ToL2CrossDomainMessenger(_L2_TO_L2_CROSS_DOMAIN_MESSENGER).relayMessage(_identifiers[_i], _payloads[_i]);
+ }
+
+ assertEq(ForTestCounter(_TARGET).getCounter(), _successfulMessages, 'Counter should have been increased');
+ }
+}
diff --git a/test/unit/CrossL2Inbox.t.sol b/test/unit/CrossL2Inbox.t.sol
new file mode 100644
index 0000000..ba3e80b
--- /dev/null
+++ b/test/unit/CrossL2Inbox.t.sol
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.25;
+
+import {ERC1967Proxy} from '@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol';
+import {Initializable} from '@openzeppelin/contracts/proxy/utils/Initializable.sol';
+import {CrossL2Inbox, Identifier, InvalidHash, InvalidRelayer, OwnableUpgradeable} from 'contracts/CrossL2Inbox.sol';
+import {Test} from 'forge-std/Test.sol';
+
+contract ForTestCrossL2Inbox is CrossL2Inbox {
+ /// @notice Test helper function to set the relayer status.
+ function forTest_setRelayer(address _RELAYER, bool _status) public {
+ validRelayers[_RELAYER] = _status;
+ }
+
+ /// @notice Test helper function to set the hash status.
+ function forTest_setValidHash(bytes32 _hash, bool _status) public {
+ validHashes[_hash] = _status;
+ }
+
+ function forTest_authorizeUpgrade(
+ address _newOwner
+ ) external {
+ _authorizeUpgrade(_newOwner);
+ }
+}
+
+abstract contract Base is Test {
+ event UpdatedRelayer(address indexed account, bool isRelayer);
+ event RegisteredHash(bytes32 indexed hash);
+
+ address internal immutable _INBOX_OWNER = makeAddr('Inbox Owner');
+ address internal immutable _RELAYER = makeAddr('Relayer');
+ address internal immutable _UNAUTHORIZED_CALLER = makeAddr('Unauthorized Caller');
+ ForTestCrossL2Inbox internal _inbox;
+ address internal _implementation;
+
+ function setUp() public virtual {
+ _implementation = address(new ForTestCrossL2Inbox());
+
+ _inbox = ForTestCrossL2Inbox(
+ address(new ERC1967Proxy(_implementation, abi.encodeWithSelector(CrossL2Inbox.initialize.selector, _INBOX_OWNER)))
+ );
+
+ vm.prank(_INBOX_OWNER);
+ _inbox.enableRelayer(_RELAYER);
+ }
+}
+
+contract CrossL2Inbox_Unit_Initialize is Base {
+ /// @notice Test that the `initialize` function sets the owner of the inbox.
+ function test_intializeNewProxy() public {
+ // Deploy a new inbox proxy.
+ ForTestCrossL2Inbox _newInbox = ForTestCrossL2Inbox(address(new ERC1967Proxy(_implementation, bytes(''))));
+
+ // Ensure the owner is not set.
+ assertEq(_newInbox.owner(), address(0));
+
+ // Initialize the inbox.
+ _newInbox.initialize(_INBOX_OWNER);
+
+ // Ensure the owner is set.
+ assertEq(_newInbox.owner(), _INBOX_OWNER);
+ }
+
+ /// @notice Test that the constructor disables the initialize function on implementation contract.
+ function test_initializeAfterDeployment() public {
+ vm.expectRevert(Initializable.InvalidInitialization.selector);
+ ForTestCrossL2Inbox(_implementation).initialize(_INBOX_OWNER);
+ }
+
+ /// @notice Test that the `initialize` function reverts when the inbox is already initialized.
+ function test_initializeWhenAlreadyInitialized(
+ address _newOwner
+ ) public {
+ vm.expectRevert(Initializable.InvalidInitialization.selector);
+ _inbox.initialize(_newOwner);
+ }
+
+ /// @notice Test that the `initialize` called on `upgradeToAndcall` in the setup function sets the owner of the inbox.
+ function test_initializeSetsOwner() public view {
+ assertEq(_inbox.owner(), _INBOX_OWNER);
+ }
+}
+
+contract CrossL2Inbox_Unit_ValidateMessage is Base {
+ event ExecutingMessage(bytes32 indexed msgHash, Identifier id);
+
+ /// @notice Test that the `validateMessage` function reverts when the hash is invalid.
+ function test_validateCheckHashIsNotTrue(Identifier calldata _id, bytes32 _msgHash) public {
+ // it reverts
+ // Ensure the hash is not valid.
+ _inbox.forTest_setValidHash(keccak256(abi.encode(_id, _msgHash)), false);
+
+ vm.expectRevert(abi.encodeWithSelector(InvalidHash.selector));
+ _inbox.validateMessage(_id, _msgHash);
+ }
+
+ function test_validateMessageWhenSucceedsEmits(Identifier calldata _id, bytes32 _msgHash) public {
+ /// Ensure the hash is valid.
+ _inbox.forTest_setValidHash(keccak256(abi.encode(_id, _msgHash)), true);
+
+ vm.expectEmit(address(_inbox));
+ emit ExecutingMessage(_msgHash, _id);
+
+ vm.prank(_RELAYER);
+ _inbox.validateMessage(_id, _msgHash);
+ }
+}
+
+contract CrossL2Inbox_Unit_EnableRelayer is Base {
+ /// @notice Test that the `enableRelayer` function reverts when the caller is not the owner.
+ function test_enableRelayerWhenCallerNotOwner(
+ address _newRelayer
+ ) public {
+ vm.prank(_UNAUTHORIZED_CALLER);
+ // it reverts
+ vm.expectRevert(
+ abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, _UNAUTHORIZED_CALLER)
+ );
+ _inbox.enableRelayer(_newRelayer);
+ }
+
+ /// @notice Test that the `enableRelayer` function sets the relayer status to true.
+ function test_enableRelayerWhenSucceeds(
+ address _newRelayer
+ ) public {
+ // it should set validRelayers[_newRelayer] to true
+ // Ensure the relayer is not enabled.
+ _inbox.forTest_setRelayer(_newRelayer, false);
+
+ vm.expectEmit();
+ emit UpdatedRelayer(_newRelayer, true);
+
+ vm.prank(_INBOX_OWNER);
+ _inbox.enableRelayer(_newRelayer);
+ assertTrue(_inbox.validRelayers(_newRelayer), 'Relayer not enabled');
+ }
+}
+
+contract CrossL2Inbox_Unit_DisableRelayer is Base {
+ /// @notice Test that the `disableRelayer` function reverts when the caller is not the owner.
+ function test_disableRelayerWhenCallerNotOwner(
+ address _relayerToDisable
+ ) public {
+ vm.prank(_UNAUTHORIZED_CALLER);
+ // it reverts
+ vm.expectRevert(
+ abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, _UNAUTHORIZED_CALLER)
+ );
+ _inbox.disableRelayer(_relayerToDisable);
+ }
+
+ /// @notice Test that the `disableRelayer` function sets the relayer status to false.
+ function test_disableRelayerWhenSucceeds(
+ address _relayerToDisable
+ ) public {
+ // it should set validRelayers[_RELAYER] to false
+ // Ensure the relayer is enabled.
+ _inbox.forTest_setRelayer(_relayerToDisable, true);
+
+ vm.expectEmit();
+ emit UpdatedRelayer(_relayerToDisable, false);
+
+ vm.prank(_INBOX_OWNER);
+ _inbox.disableRelayer(_relayerToDisable);
+ assertFalse(_inbox.validRelayers(_relayerToDisable), 'Relayer not disabled');
+ }
+}
+
+contract CrossL2Inbox_Unit_RegisterValidHash is Base {
+ /// @notice Test that the `registerValidHash` function reverts when the caller is not a relayer.
+ function test_registerValidHashWhenCallerNotRelayer(
+ bytes32 _hash
+ ) public {
+ _inbox.forTest_setValidHash(_hash, false);
+ // it reverts
+ vm.prank(_UNAUTHORIZED_CALLER);
+ vm.expectRevert(abi.encodeWithSelector(InvalidRelayer.selector));
+ _inbox.registerValidHash(_hash);
+ }
+
+ /// @notice Test that the `registerValidHash` function registers a valid hash.
+ function test_registerValidHashWhenSucceeds(
+ bytes32 _hash
+ ) public {
+ vm.expectEmit();
+ emit RegisteredHash(_hash);
+
+ vm.prank(_RELAYER);
+ _inbox.registerValidHash(_hash);
+ assertTrue(_inbox.validHashes(_hash), 'Hash not registered');
+ }
+}
+
+contract CrossL2Inbox_Unit_AuthorizeUpgrade is Base {
+ /// @notice Test authorizeUpgrade reverts if the sender is not the owner
+ function test_authorizeUpgradeWhenNotOwner(
+ address _sender
+ ) external {
+ vm.prank(_UNAUTHORIZED_CALLER);
+ vm.expectRevert(
+ abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, _UNAUTHORIZED_CALLER)
+ );
+ _inbox.forTest_authorizeUpgrade(_sender);
+ }
+
+ /// @notice Test authorizeUpgrade if the sender is the owner
+ function test_authorizeUpgradeWhenOwner() external {
+ vm.prank(_INBOX_OWNER);
+ _inbox.forTest_authorizeUpgrade(_INBOX_OWNER);
+ }
+}
diff --git a/test/unit/CrossL2Inbox.tree b/test/unit/CrossL2Inbox.tree
new file mode 100644
index 0000000..416134b
--- /dev/null
+++ b/test/unit/CrossL2Inbox.tree
@@ -0,0 +1,38 @@
+.
+├── CrossL2Inbox::initialize
+│ ├── new uninitialized proxy
+│ │ └── it sets owner
+│ ├── after deployment
+│ │ └── it reverts on implementation
+│ ├── when already initialized
+│ │ └── it reverts
+│ └── when succeeds
+│ └── it sets owner
+├── CrossL2Inbox::validateMessage
+│ ├── when _checkHash() is not true
+│ │ └── it reverts
+│ └── when succeeds
+│ └── emits ExecutingMessage
+├── CrossL2Inbox::enableRelayer
+│ ├── when caller is not owner
+│ │ └── it reverts
+│ └── when succeeds
+│ ├── it emits UpdatedRelayer event
+│ └── it should set validRelayers[_relayer] to true
+├── CrossL2Inbox::disableRelayer
+│ ├── when caller is not owner
+│ │ └── it reverts
+│ └── when succeeds
+│ ├── it emits UpdatedRelayer event
+│ └── it should set validRelayers[_relayer] to false
+├── CrossL2Inbox::registerValidHash
+│ ├── when caller is not validRelayers[_relayer]
+│ │ └── it reverts
+│ └── when succeeds
+│ ├── it emits RegisteredHash event
+│ └── it should set validHashes[_hash] to true
+└── CrossL2Inbox::_authorizeUpgrade
+ ├── when called from not owner
+ │ └── it reverts
+ └── when called from owner
+ └── it succeeds
\ No newline at end of file
diff --git a/test/unit/L2ToL2CrossDomainMessenger.t.sol b/test/unit/L2ToL2CrossDomainMessenger.t.sol
new file mode 100644
index 0000000..abc9a15
--- /dev/null
+++ b/test/unit/L2ToL2CrossDomainMessenger.t.sol
@@ -0,0 +1,503 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.25;
+
+import {Helpers} from '../utils/Helpers.sol';
+import {ERC1967Proxy} from '@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol';
+import {Initializable} from '@openzeppelin/contracts/proxy/utils/Initializable.sol';
+import {CrossL2Inbox, Identifier, InvalidHash} from 'contracts/CrossL2Inbox.sol';
+import {
+ EventPayloadNotSentMessage,
+ IdOriginNotL2ToL2CrossDomainMessenger,
+ L2ToL2CrossDomainMessenger,
+ MessageAlreadyRelayed,
+ MessageDestinationNotRelayChain,
+ MessageDestinationSameChain,
+ MessageTargetCrossL2Inbox,
+ MessageTargetL2ToL2CrossDomainMessenger,
+ OwnableUpgradeable,
+ TargetCallFailed,
+ TransientReentrancyAware
+} from 'contracts/L2ToL2CrossDomainMessenger.sol';
+import {Test} from 'forge-std/Test.sol';
+import {Hashing} from 'optimism/src/libraries/Hashing.sol';
+
+contract ForTestL2ToL2CrossDomainMessenger is L2ToL2CrossDomainMessenger {
+ constructor(
+ address _inbox
+ ) L2ToL2CrossDomainMessenger(_inbox) {}
+
+ function forTest_setSuccessfulMessage(bytes32 _messageHash, bool _relayed) external {
+ successfulMessages[_messageHash] = _relayed;
+ }
+
+ function forTest_storeMessageMetadata(uint256 _source, address _sender) external {
+ assembly {
+ tstore(CROSS_DOMAIN_MESSAGE_SENDER_SLOT, _sender)
+ tstore(CROSS_DOMAIN_MESSAGE_SOURCE_SLOT, _source)
+ }
+ }
+
+ function forTest_setEntered(
+ uint256 _entered
+ ) external {
+ assembly {
+ tstore(ENTERED_SLOT, _entered)
+ }
+ }
+
+ function forTest_entered() external view returns (bool entered_) {
+ entered_ = _entered();
+ }
+
+ function forTest_authorizeUpgrade(
+ address _newOwner
+ ) external {
+ _authorizeUpgrade(_newOwner);
+ }
+}
+
+abstract contract Base is Test {
+ /// @notice Added so we can filter out these as valid targets for messages
+ address internal constant _CHEATCODES_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;
+ address internal constant _CONSOLE_ADDRESS = 0x000000000000000000636F6e736F6c652e6c6f67;
+
+ address internal immutable _MESSENGER_OWNER = makeAddr('Messenger Owner');
+ address internal immutable _UNAUTHORIZED_CALLER = makeAddr('Unauthorized Caller');
+ address internal immutable _MOCK_INBOX = makeAddr('CrossL2Inbox');
+ ForTestL2ToL2CrossDomainMessenger internal _cdm;
+ address internal _implementation;
+
+ function setUp() external {
+ _implementation = address(new ForTestL2ToL2CrossDomainMessenger(_MOCK_INBOX));
+
+ _cdm = ForTestL2ToL2CrossDomainMessenger(
+ address(
+ new ERC1967Proxy(
+ _implementation, abi.encodeWithSelector(L2ToL2CrossDomainMessenger.initialize.selector, _MESSENGER_OWNER)
+ )
+ )
+ );
+ }
+
+ function _mockAndExpect(address _target, bytes memory _calldata, bytes memory _returnValue) internal {
+ vm.mockCall(_target, _calldata, _returnValue);
+ vm.expectCall(_target, _calldata);
+ }
+}
+
+contract L2ToL2CrossDomainMessenger_Unit_Constructor is Base {
+ /// @notice Test constructor sets CROSS_L2_INBOX
+ function test_constructor(
+ address _crossL2Inbox
+ ) external {
+ vm.assume(_crossL2Inbox != address(0));
+ L2ToL2CrossDomainMessenger _crossDomainMessenger = new L2ToL2CrossDomainMessenger(_crossL2Inbox);
+
+ assertEq(_crossDomainMessenger.CROSS_L2_INBOX(), _crossL2Inbox, 'Incorrect CROSS_L2_INBOX');
+ }
+}
+
+contract L2ToL2CrossDomainMessenger_Unit_Intialize is Base {
+ /// @notice Test that the `initialize` function sets the owner of the inbox.
+ function test_intializeNewProxy() public {
+ // Deploy a new messenger proxy.
+ ForTestL2ToL2CrossDomainMessenger _newMessenger =
+ ForTestL2ToL2CrossDomainMessenger(address(new ERC1967Proxy(_implementation, bytes(''))));
+
+ // Ensure the owner is not set.
+ assertEq(_newMessenger.owner(), address(0));
+
+ // Initialize the inbox.
+ _newMessenger.initialize(_MESSENGER_OWNER);
+
+ // Ensure the owner is set.
+ assertEq(_newMessenger.owner(), _MESSENGER_OWNER);
+ }
+
+ /// @notice Test that the constructor disables the initialize function on implentation contract.
+ function test_initializeAfterDeployment() public {
+ vm.expectRevert(Initializable.InvalidInitialization.selector);
+ L2ToL2CrossDomainMessenger(_implementation).initialize(_MESSENGER_OWNER);
+ }
+
+ /// @notice Test initialize reverts if the contract is already initialized
+ function test_initializeWhenIsInitialized() external {
+ vm.expectRevert(Initializable.InvalidInitialization.selector);
+ _cdm.initialize(_MESSENGER_OWNER);
+ }
+
+ /// @notice Test initialize sets the owner at deployment
+ function test_initializeSetsOwner() external view {
+ assertEq(_cdm.owner(), _MESSENGER_OWNER);
+ }
+}
+
+contract L2ToL2CrossDomainMessenger_Unit_CrossDomainMessageSender is Base {
+ /// @notice Test that the sender is correctly set in the transient storage
+ function test_crossDomainMessageSenderReturnsCorrect(
+ address _sender
+ ) external {
+ _cdm.forTest_setEntered(1);
+ assertTrue(_cdm.forTest_entered(), 'Not entered');
+ _cdm.forTest_storeMessageMetadata(0, _sender);
+ assertEq(_cdm.crossDomainMessageSender(), _sender, 'Incorrect sender');
+ }
+
+ /// @notice Test that crossDomainMessageSender reverts if not called in the middle of `relayMessage` or `sendMessage`
+ function test_crossDomainMessageSenderWhenNotEnteredReverts() external {
+ vm.expectRevert();
+ _cdm.crossDomainMessageSender();
+ }
+}
+
+contract L2ToL2CrossDomainMessenger_Unit_CrossDomainMessageSource is Base {
+ /// @notice Test that the source chain is correctly set in the transient storage
+ function test_crossDomainMessageSourceReturnsCorrect(
+ uint256 _source
+ ) external {
+ _cdm.forTest_setEntered(1);
+ _cdm.forTest_storeMessageMetadata(_source, address(0));
+ assertEq(_cdm.crossDomainMessageSource(), _source, 'Incorrect source');
+ }
+
+ /// @notice Test that crossDomainMessageSource reverts if not called in the middle of `relayMessage` or `sendMessage`
+ function test_crossDomainMessageSourceWhenNotEnteredReverts() external {
+ vm.expectRevert();
+ _cdm.crossDomainMessageSource();
+ }
+}
+
+contract L2ToL2CrossDomainMessenger_Unit_CrossDomainMessageContext is Base {
+ /// @notice Test that the sender and source chain are correctly set in the transient storage
+ function test_crossDomainMessageContextReturnsCorrect(uint256 _source, address _sender) external {
+ _cdm.forTest_setEntered(1);
+ _cdm.forTest_storeMessageMetadata(_source, _sender);
+ (address _storedSender, uint256 _storedSource) = _cdm.crossDomainMessageContext();
+ assertEq(_storedSender, _sender, 'Incorrect sender');
+ assertEq(_storedSource, _source, 'Incorrect source');
+ }
+
+ /// @notice Test that crossDomainMessageContext reverts if not called in the middle of `relayMessage` or `sendMessage`
+ function test_crossDomainMessageContextWhenNotEnteredReverts() external {
+ vm.expectRevert();
+ _cdm.crossDomainMessageContext();
+ }
+}
+
+contract L2ToL2CrossDomainMessenger_Unit_SendMessage is Base {
+ // Event sent on sendMessage success
+ event SentMessage(
+ uint256 indexed destination, address indexed target, uint256 indexed messageNonce, address sender, bytes message
+ );
+
+ /// @notice Test sendMessage reverts if sending a message to the same chain
+ function test_sendMessageWhenDestinationSameChain(address _target, bytes memory _message) external {
+ // it reverts
+ uint256 _thisChainId = block.chainid;
+
+ vm.expectRevert(MessageDestinationSameChain.selector);
+ _cdm.sendMessage(_thisChainId, _target, _message);
+ }
+
+ /// @notice Test sendMessage reverts if sending a message with CrossL2Inbox as target
+ function test_sendMessageWhenTargetIsInbox(uint256 _destinationChain, bytes memory _message) external {
+ // it reverts
+ vm.assume(_destinationChain != block.chainid);
+ address _target = address(_MOCK_INBOX);
+
+ vm.expectRevert(MessageTargetCrossL2Inbox.selector);
+ _cdm.sendMessage(_destinationChain, _target, _message);
+ }
+
+ /// @notice Test sendMessage reverts if sending a message with L2ToL2CrossDomainMessenger as target
+ function test_sendMessageWhenTargetIsCrossDomainMessenger(uint256 _destinationChain, bytes memory _message) external {
+ vm.assume(_destinationChain != block.chainid);
+ address _target = address(_cdm);
+
+ vm.expectRevert(MessageTargetL2ToL2CrossDomainMessenger.selector);
+ _cdm.sendMessage(_destinationChain, _target, _message);
+ }
+
+ /// @notice Test sendMessage emits SentMessage event on success
+ function test_sendMessageWhenSucceeds(uint256 _destId, address _target, bytes memory _data) external {
+ vm.assume(_destId != block.chainid);
+ vm.assume(_target != address(_cdm));
+ vm.assume(_target != _MOCK_INBOX);
+
+ uint256 _nonceBefore = _cdm.messageNonce();
+
+ vm.expectEmit(address(_cdm));
+ emit SentMessage(_destId, _target, _nonceBefore, address(this), _data);
+
+ bytes32 _expectedHash = Hashing.hashL2toL2CrossDomainMessage({
+ _destination: _destId,
+ _source: block.chainid,
+ _nonce: _nonceBefore,
+ _sender: address(this),
+ _target: _target,
+ _message: _data
+ });
+
+ bytes32 _hash = _cdm.sendMessage(_destId, _target, _data);
+
+ assertEq(_expectedHash, _hash);
+ assertEq(_cdm.messageNonce(), _nonceBefore + 1);
+ }
+}
+
+contract L2ToL2CrossDomainMessenger_Unit_RelayMessage is Base {
+ // Event sent on relayMessage success
+ event RelayedMessage(uint256 indexed source, uint256 indexed messageNonce, bytes32 indexed messageHash);
+
+ /// @notice Test relayMessage can not be re-entered
+ function test_relayMessageWhenEntered(
+ Identifier memory _id
+ ) external {
+ // it reverts
+ _cdm.forTest_setEntered(1);
+
+ vm.expectRevert(TransientReentrancyAware.ReentrantCall.selector);
+ _cdm.relayMessage(_id, bytes(''));
+ }
+
+ /// @notice Test relayMessage reverts if the message was not originated in the CDM
+ function test_relayMessageWhenOriginNotCrossDomainMessenger(
+ Identifier memory _id
+ ) external {
+ // it reverts
+ vm.assume(_id.origin != address(_cdm));
+
+ vm.expectRevert(IdOriginNotL2ToL2CrossDomainMessenger.selector);
+ _cdm.relayMessage(_id, bytes(''));
+ }
+
+ /// @notice Test relayMessage reverts if wrong event signature prefixes payload
+ function test_relayMessageWhenWrongEventPrefix(
+ Identifier memory _id,
+ address _target,
+ address _sender,
+ uint256 _nonce,
+ bytes memory _message
+ ) external {
+ // it reverts
+ vm.assume(_target != address(_cdm));
+ vm.assume(_target != _MOCK_INBOX);
+
+ _id.origin = address(_cdm);
+
+ bytes memory _payload = abi.encodePacked(
+ keccak256(abi.encodeWithSignature('NotSentMessage()')),
+ abi.encode(_id.chainId, _target, _nonce),
+ abi.encode(_sender, _message)
+ );
+
+ bytes32 _messageHash = Hashing.hashL2toL2CrossDomainMessage({
+ _destination: block.chainid,
+ _source: _id.chainId,
+ _nonce: _nonce,
+ _sender: _sender,
+ _target: _target,
+ _message: _message
+ });
+
+ _cdm.forTest_setSuccessfulMessage(_messageHash, true);
+
+ bytes memory _validateCalldata = abi.encodeCall(CrossL2Inbox.validateMessage, (_id, keccak256(_payload)));
+ _mockAndExpect(_MOCK_INBOX, _validateCalldata, abi.encode(true));
+
+ vm.expectRevert(EventPayloadNotSentMessage.selector);
+ _cdm.relayMessage(_id, _payload);
+ }
+
+ /// @notice Test relayMessage reverts if this chain is not the destination chain on the message
+ function test_relayMessageWhenDestinationNotThisChain(
+ Identifier memory _id,
+ address _target,
+ bytes memory _message,
+ uint256 _nonce
+ ) external {
+ vm.assume(_id.chainId != block.chainid);
+ // it reverts
+ _id.origin = address(_cdm);
+
+ bytes memory _payload = Helpers.encodePayload(_id.chainId, _target, _nonce, _id.origin, _message);
+
+ bytes memory _validateCalldata = abi.encodeCall(CrossL2Inbox.validateMessage, (_id, keccak256(_payload)));
+ _mockAndExpect(_MOCK_INBOX, _validateCalldata, abi.encode(true));
+
+ vm.expectRevert(MessageDestinationNotRelayChain.selector);
+ _cdm.relayMessage(_id, _payload);
+ }
+
+ /// @notice Test relayMessage reverts if the CrossL2Inbox is the target of the message
+ function test_relayMessageWhenTargetIsInbox(Identifier memory _id, bytes memory _message, uint256 _nonce) external {
+ _id.origin = address(_cdm);
+
+ bytes memory _payload = Helpers.encodePayload(block.chainid, address(_MOCK_INBOX), _nonce, _id.origin, _message);
+
+ bytes memory _validateCalldata = abi.encodeCall(CrossL2Inbox.validateMessage, (_id, keccak256(_payload)));
+ _mockAndExpect(_MOCK_INBOX, _validateCalldata, abi.encode(true));
+
+ vm.expectRevert(MessageTargetCrossL2Inbox.selector);
+ _cdm.relayMessage(_id, _payload);
+ }
+
+ /// @notice Test relayMessage reverts if the CDM is the target of the message
+ function test_relayMessageWhenTargetL2ToL2CrossDomainMessenger(
+ Identifier memory _id,
+ uint256 _nonce,
+ bytes memory _message
+ ) external {
+ // it reverts
+ _id.origin = address(_cdm);
+
+ bytes memory _payload = Helpers.encodePayload(block.chainid, address(_cdm), _nonce, _id.origin, _message);
+
+ bytes memory _validateCalldata = abi.encodeCall(CrossL2Inbox.validateMessage, (_id, keccak256(_payload)));
+ _mockAndExpect(_MOCK_INBOX, _validateCalldata, abi.encode(true));
+
+ vm.expectRevert(MessageTargetL2ToL2CrossDomainMessenger.selector);
+ _cdm.relayMessage(_id, _payload);
+ }
+
+ /// @notice Test relayMessage reverts if the CrossL2Inbox reverts with InvalidHash
+ function test_relayMessageWhenInboxReturnsInvalidHash(
+ Identifier memory _id,
+ address _target,
+ bytes memory _payload
+ ) external {
+ // it reverts
+ vm.assume(_target != address(_cdm));
+ vm.assume(_target != _MOCK_INBOX);
+
+ _id.origin = address(_cdm);
+
+ vm.etch(_MOCK_INBOX, bytes('Code'));
+
+ bytes memory _validateCalldata = abi.encodeCall(CrossL2Inbox.validateMessage, (_id, keccak256(_payload)));
+ vm.mockCallRevert(_MOCK_INBOX, _validateCalldata, abi.encodeWithSelector(InvalidHash.selector));
+
+ vm.expectRevert(InvalidHash.selector);
+ _cdm.relayMessage(_id, _payload);
+ }
+
+ /// @notice Test relayMessage reverts if the message has been already relayed
+ function test_relayMessageWhenMessageAlreadyRelayed(
+ Identifier memory _id,
+ address _target,
+ address _sender,
+ uint256 _nonce,
+ bytes memory _message
+ ) external {
+ // it reverts
+ vm.assume(_target != address(_cdm));
+ vm.assume(_target != _MOCK_INBOX);
+
+ _id.origin = address(_cdm);
+
+ bytes memory _payload = Helpers.encodePayload(block.chainid, _target, _nonce, _sender, _message);
+
+ bytes32 _messageHash = Hashing.hashL2toL2CrossDomainMessage({
+ _destination: block.chainid,
+ _source: _id.chainId,
+ _nonce: _nonce,
+ _sender: _sender,
+ _target: _target,
+ _message: _message
+ });
+
+ _cdm.forTest_setSuccessfulMessage(_messageHash, true);
+
+ bytes memory _validateCalldata = abi.encodeCall(CrossL2Inbox.validateMessage, (_id, keccak256(_payload)));
+ _mockAndExpect(_MOCK_INBOX, _validateCalldata, abi.encode(true));
+
+ vm.expectRevert(MessageAlreadyRelayed.selector);
+ _cdm.relayMessage(_id, _payload);
+ }
+
+ /// @notice Test relayMessage reverts if the call to the target fails
+ function test_relayMessageWhenTargetCallFails(
+ Identifier memory _id,
+ address _target,
+ address _sender,
+ uint256 _nonce,
+ bytes memory _message
+ ) external {
+ // it reverts
+ vm.assume(_target != address(_cdm));
+ vm.assume(_target != _MOCK_INBOX);
+
+ _id.origin = address(_cdm);
+
+ bytes memory _payload = Helpers.encodePayload(block.chainid, _target, _nonce, _sender, _message);
+
+ bytes memory _validateCalldata = abi.encodeCall(CrossL2Inbox.validateMessage, (_id, keccak256(_payload)));
+ _mockAndExpect(_MOCK_INBOX, _validateCalldata, abi.encode(true));
+
+ vm.mockCallRevert(_target, _message, 'SOME_ERROR_MESSAGE');
+
+ vm.expectRevert(TargetCallFailed.selector);
+ _cdm.relayMessage(_id, _payload);
+ }
+
+ /// @notice Test relayMessage updates the successfulMessages mapping and emits RelayedMessage event on success
+ function test_relayMessageWhenSucceeds(
+ Identifier memory _id,
+ address _target,
+ address _sender,
+ uint256 _nonce,
+ bytes memory _message
+ ) external {
+ vm.assume(_target != _CHEATCODES_ADDRESS && _target != _CONSOLE_ADDRESS);
+
+ vm.assume(_target != address(_cdm));
+ vm.assume(_target != _MOCK_INBOX);
+
+ _id.origin = address(_cdm);
+
+ bytes memory _payload = Helpers.encodePayload(block.chainid, _target, _nonce, _sender, _message);
+
+ bytes memory _validateCalldata = abi.encodeCall(CrossL2Inbox.validateMessage, (_id, keccak256(_payload)));
+ _mockAndExpect(_MOCK_INBOX, _validateCalldata, abi.encode(true));
+
+ _mockAndExpect(_target, _message, abi.encode(true));
+
+ bytes32 _messageHash = Hashing.hashL2toL2CrossDomainMessage({
+ _destination: block.chainid,
+ _source: _id.chainId,
+ _nonce: _nonce,
+ _sender: _sender,
+ _target: _target,
+ _message: _message
+ });
+
+ assertFalse(_cdm.successfulMessages(_messageHash), 'Message already relayed');
+
+ vm.expectEmit(address(_cdm));
+ emit RelayedMessage(_id.chainId, _nonce, _messageHash);
+
+ _cdm.relayMessage(_id, _payload);
+
+ // successfulMessages must have been updated
+ assertTrue(_cdm.successfulMessages(_messageHash), 'Message not relayed');
+ }
+}
+
+contract L2ToL2CrossDomainMessenger_Unit_AuthorizeUpgrade is Base {
+ /// @notice Test authorizeUpgrade reverts if the sender is not the owner
+ function test_authorizeUpgradeWhenNotOwner(
+ address _sender
+ ) external {
+ vm.prank(_UNAUTHORIZED_CALLER);
+ vm.expectRevert(
+ abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, _UNAUTHORIZED_CALLER)
+ );
+ _cdm.forTest_authorizeUpgrade(_sender);
+ }
+
+ /// @notice Test authorizeUpgrade if the sender is the owner
+ function test_authorizeUpgradeWhenOwner() external {
+ vm.prank(_MESSENGER_OWNER);
+ _cdm.forTest_authorizeUpgrade(_MESSENGER_OWNER);
+ }
+}
diff --git a/test/unit/L2ToL2CrossDomainMessenger.tree b/test/unit/L2ToL2CrossDomainMessenger.tree
new file mode 100644
index 0000000..306b991
--- /dev/null
+++ b/test/unit/L2ToL2CrossDomainMessenger.tree
@@ -0,0 +1,66 @@
+.
+├── L2ToL2CrossDomainMessenger::constructor
+│ └── after deployment
+│ └── CROSS_L2_INBOX is set
+├── L2ToL2CrossDomainMessenger::initialize
+│ ├── new uninitialized proxy
+│ │ └── it sets owner
+│ ├── after deployment
+│ │ └── it reverts on implementation
+│ ├── when already initialized
+│ │ └── it reverts
+│ └── when succeeds
+│ └── it sets owner
+├── L2ToL2CrossDomainMessenger::crossDomainMessageSender
+│ ├── when called in the middle of execution
+│ │ └── it returns the sender
+│ └── when called not in the middle of execution
+│ └── it reverts
+├── L2ToL2CrossDomainMessenger::crossDomainMessageSource
+│ ├── when called in the middle of execution
+│ │ └── it returns the source
+│ └── when called not in the middle of execution
+│ └── it reverts
+├── L2ToL2CrossDomainMessenger::crossDomainMessageContext
+│ ├── when called in the middle of execution
+│ │ └── it returns the sender and source
+│ └── when called not in the middle of execution
+│ └── it reverts
+├── L2ToL2CrossDomainMessenger::sendMessage
+│ ├── when destination is the same chain
+│ │ └── it reverts
+│ ├── when target address is the CrossL2Inbox
+│ │ └── it reverts
+│ ├── when target address is L2ToL2CrossDomainMessenger
+│ │ └── it reverts
+│ └── when succeeds
+│ ├── it should increase msgNonce
+│ └── it should emit SentMessage event
+├── L2ToL2CrossDomainMessenger::relayMessage
+│ ├── when entered
+│ │ └── it reverts
+│ ├── when _id.origin is not this address
+│ │ └── it reverts
+│ ├── When wrong prefix on message
+│ │ └── it reverts
+│ ├── when destination is not this chain
+│ │ └── it reverts
+│ ├── when target address is the CrossL2Inbox
+│ │ └── it reverts
+│ ├── when target address is L2ToL2CrossDomainMessenger
+│ │ └── it reverts
+│ ├── when inbox returns InvalidHash
+│ │ └── it reverts
+│ ├── when message has been relayed already
+│ │ └── it reverts
+│ ├── when call to target fails
+│ │ └── it reverts
+│ └── when succeeds
+│ ├── it calls target with correct parameters
+│ ├── it updates successfulMessages
+│ └── emits RelayedMessage
+└── L2ToL2CrossDomainMessenger::_authorizeUpgrade
+ ├── when called from not owner
+ │ └── it reverts
+ └── when called from owner
+ └── it succeeds
\ No newline at end of file
diff --git a/test/utils/Helpers.sol b/test/utils/Helpers.sol
new file mode 100644
index 0000000..3ff9df5
--- /dev/null
+++ b/test/utils/Helpers.sol
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.25;
+
+library Helpers {
+ bytes32 internal constant _SENT_MESSAGE_EVENT_SELECTOR =
+ 0x382409ac69001e11931a28435afef442cbfd20d9891907e8fa373ba7d351f320;
+
+ function encodePayload(
+ uint256 _destination,
+ address _target,
+ uint256 _nonce,
+ address _sender,
+ bytes memory _message
+ ) external pure returns (bytes memory encoded_) {
+ encoded_ = abi.encodePacked(
+ _SENT_MESSAGE_EVENT_SELECTOR, abi.encode(_destination, _target, _nonce), abi.encode(_sender, _message)
+ );
+ }
+}
diff --git a/yarn.lock b/yarn.lock
new file mode 100644
index 0000000..4af7674
--- /dev/null
+++ b/yarn.lock
@@ -0,0 +1,1958 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/code-frame@^7.0.0":
+ version "7.26.2"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85"
+ integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.25.9"
+ js-tokens "^4.0.0"
+ picocolors "^1.0.0"
+
+"@babel/helper-validator-identifier@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7"
+ integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
+
+"@commitlint/cli@19.3.0":
+ version "19.3.0"
+ resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-19.3.0.tgz#44e6da9823a01f0cdcc43054bbefdd2c6c5ddf39"
+ integrity sha512-LgYWOwuDR7BSTQ9OLZ12m7F/qhNY+NpAyPBgo4YNMkACE7lGuUnuQq1yi9hz1KA4+3VqpOYl8H1rY/LYK43v7g==
+ dependencies:
+ "@commitlint/format" "^19.3.0"
+ "@commitlint/lint" "^19.2.2"
+ "@commitlint/load" "^19.2.0"
+ "@commitlint/read" "^19.2.1"
+ "@commitlint/types" "^19.0.3"
+ execa "^8.0.1"
+ yargs "^17.0.0"
+
+"@commitlint/config-conventional@19.2.2":
+ version "19.2.2"
+ resolved "https://registry.yarnpkg.com/@commitlint/config-conventional/-/config-conventional-19.2.2.tgz#1f4e6975d428985deacf2b3ff6547e02c9302054"
+ integrity sha512-mLXjsxUVLYEGgzbxbxicGPggDuyWNkf25Ht23owXIH+zV2pv1eJuzLK3t1gDY5Gp6pxdE60jZnWUY5cvgL3ufw==
+ dependencies:
+ "@commitlint/types" "^19.0.3"
+ conventional-changelog-conventionalcommits "^7.0.2"
+
+"@commitlint/config-validator@^19.5.0":
+ version "19.5.0"
+ resolved "https://registry.yarnpkg.com/@commitlint/config-validator/-/config-validator-19.5.0.tgz#f0a4eda2109fc716ef01bb8831af9b02e3a1e568"
+ integrity sha512-CHtj92H5rdhKt17RmgALhfQt95VayrUo2tSqY9g2w+laAXyk7K/Ef6uPm9tn5qSIwSmrLjKaXK9eiNuxmQrDBw==
+ dependencies:
+ "@commitlint/types" "^19.5.0"
+ ajv "^8.11.0"
+
+"@commitlint/ensure@^19.5.0":
+ version "19.5.0"
+ resolved "https://registry.yarnpkg.com/@commitlint/ensure/-/ensure-19.5.0.tgz#b087374a6a0a0140e5925a82901d234885d9f6dd"
+ integrity sha512-Kv0pYZeMrdg48bHFEU5KKcccRfKmISSm9MvgIgkpI6m+ohFTB55qZlBW6eYqh/XDfRuIO0x4zSmvBjmOwWTwkg==
+ dependencies:
+ "@commitlint/types" "^19.5.0"
+ lodash.camelcase "^4.3.0"
+ lodash.kebabcase "^4.1.1"
+ lodash.snakecase "^4.1.1"
+ lodash.startcase "^4.4.0"
+ lodash.upperfirst "^4.3.1"
+
+"@commitlint/execute-rule@^19.5.0":
+ version "19.5.0"
+ resolved "https://registry.yarnpkg.com/@commitlint/execute-rule/-/execute-rule-19.5.0.tgz#c13da8c03ea0379f30856111e27d57518e25b8a2"
+ integrity sha512-aqyGgytXhl2ejlk+/rfgtwpPexYyri4t8/n4ku6rRJoRhGZpLFMqrZ+YaubeGysCP6oz4mMA34YSTaSOKEeNrg==
+
+"@commitlint/format@^19.3.0":
+ version "19.5.0"
+ resolved "https://registry.yarnpkg.com/@commitlint/format/-/format-19.5.0.tgz#d879db2d97d70ae622397839fb8603d56e85a250"
+ integrity sha512-yNy088miE52stCI3dhG/vvxFo9e4jFkU1Mj3xECfzp/bIS/JUay4491huAlVcffOoMK1cd296q0W92NlER6r3A==
+ dependencies:
+ "@commitlint/types" "^19.5.0"
+ chalk "^5.3.0"
+
+"@commitlint/is-ignored@^19.5.0":
+ version "19.5.0"
+ resolved "https://registry.yarnpkg.com/@commitlint/is-ignored/-/is-ignored-19.5.0.tgz#f8b7f365887acc1e3bdb31b17117bb435585dddf"
+ integrity sha512-0XQ7Llsf9iL/ANtwyZ6G0NGp5Y3EQ8eDQSxv/SRcfJ0awlBY4tHFAvwWbw66FVUaWICH7iE5en+FD9TQsokZ5w==
+ dependencies:
+ "@commitlint/types" "^19.5.0"
+ semver "^7.6.0"
+
+"@commitlint/lint@^19.2.2":
+ version "19.5.0"
+ resolved "https://registry.yarnpkg.com/@commitlint/lint/-/lint-19.5.0.tgz#f4e162e7857a1c0694b20b92527704897558ff70"
+ integrity sha512-cAAQwJcRtiBxQWO0eprrAbOurtJz8U6MgYqLz+p9kLElirzSCc0vGMcyCaA1O7AqBuxo11l1XsY3FhOFowLAAg==
+ dependencies:
+ "@commitlint/is-ignored" "^19.5.0"
+ "@commitlint/parse" "^19.5.0"
+ "@commitlint/rules" "^19.5.0"
+ "@commitlint/types" "^19.5.0"
+
+"@commitlint/load@^19.2.0":
+ version "19.5.0"
+ resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-19.5.0.tgz#67f90a294894d1f99b930b6152bed2df44a81794"
+ integrity sha512-INOUhkL/qaKqwcTUvCE8iIUf5XHsEPCLY9looJ/ipzi7jtGhgmtH7OOFiNvwYgH7mA8osUWOUDV8t4E2HAi4xA==
+ dependencies:
+ "@commitlint/config-validator" "^19.5.0"
+ "@commitlint/execute-rule" "^19.5.0"
+ "@commitlint/resolve-extends" "^19.5.0"
+ "@commitlint/types" "^19.5.0"
+ chalk "^5.3.0"
+ cosmiconfig "^9.0.0"
+ cosmiconfig-typescript-loader "^5.0.0"
+ lodash.isplainobject "^4.0.6"
+ lodash.merge "^4.6.2"
+ lodash.uniq "^4.5.0"
+
+"@commitlint/message@^19.5.0":
+ version "19.5.0"
+ resolved "https://registry.yarnpkg.com/@commitlint/message/-/message-19.5.0.tgz#c062d9a1d2b3302c3a8cac25d6d1125ea9c019b2"
+ integrity sha512-R7AM4YnbxN1Joj1tMfCyBryOC5aNJBdxadTZkuqtWi3Xj0kMdutq16XQwuoGbIzL2Pk62TALV1fZDCv36+JhTQ==
+
+"@commitlint/parse@^19.5.0":
+ version "19.5.0"
+ resolved "https://registry.yarnpkg.com/@commitlint/parse/-/parse-19.5.0.tgz#b450dad9b5a95ac5ba472d6d0fdab822dce946fc"
+ integrity sha512-cZ/IxfAlfWYhAQV0TwcbdR1Oc0/r0Ik1GEessDJ3Lbuma/MRO8FRQX76eurcXtmhJC//rj52ZSZuXUg0oIX0Fw==
+ dependencies:
+ "@commitlint/types" "^19.5.0"
+ conventional-changelog-angular "^7.0.0"
+ conventional-commits-parser "^5.0.0"
+
+"@commitlint/read@^19.2.1":
+ version "19.5.0"
+ resolved "https://registry.yarnpkg.com/@commitlint/read/-/read-19.5.0.tgz#601f9f1afe69852b0f28aa81cd455b40979fad6b"
+ integrity sha512-TjS3HLPsLsxFPQj6jou8/CZFAmOP2y+6V4PGYt3ihbQKTY1Jnv0QG28WRKl/d1ha6zLODPZqsxLEov52dhR9BQ==
+ dependencies:
+ "@commitlint/top-level" "^19.5.0"
+ "@commitlint/types" "^19.5.0"
+ git-raw-commits "^4.0.0"
+ minimist "^1.2.8"
+ tinyexec "^0.3.0"
+
+"@commitlint/resolve-extends@^19.5.0":
+ version "19.5.0"
+ resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-19.5.0.tgz#f3ec33e12d10df90cae0bfad8e593431fb61b18e"
+ integrity sha512-CU/GscZhCUsJwcKTJS9Ndh3AKGZTNFIOoQB2n8CmFnizE0VnEuJoum+COW+C1lNABEeqk6ssfc1Kkalm4bDklA==
+ dependencies:
+ "@commitlint/config-validator" "^19.5.0"
+ "@commitlint/types" "^19.5.0"
+ global-directory "^4.0.1"
+ import-meta-resolve "^4.0.0"
+ lodash.mergewith "^4.6.2"
+ resolve-from "^5.0.0"
+
+"@commitlint/rules@^19.5.0":
+ version "19.5.0"
+ resolved "https://registry.yarnpkg.com/@commitlint/rules/-/rules-19.5.0.tgz#2a72ab506d49d7f33eda56f0ae072a3479429e74"
+ integrity sha512-hDW5TPyf/h1/EufSHEKSp6Hs+YVsDMHazfJ2azIk9tHPXS6UqSz1dIRs1gpqS3eMXgtkT7JH6TW4IShdqOwhAw==
+ dependencies:
+ "@commitlint/ensure" "^19.5.0"
+ "@commitlint/message" "^19.5.0"
+ "@commitlint/to-lines" "^19.5.0"
+ "@commitlint/types" "^19.5.0"
+
+"@commitlint/to-lines@^19.5.0":
+ version "19.5.0"
+ resolved "https://registry.yarnpkg.com/@commitlint/to-lines/-/to-lines-19.5.0.tgz#e4b7f34f09064568c96a74de4f1fc9f466c4d472"
+ integrity sha512-R772oj3NHPkodOSRZ9bBVNq224DOxQtNef5Pl8l2M8ZnkkzQfeSTr4uxawV2Sd3ui05dUVzvLNnzenDBO1KBeQ==
+
+"@commitlint/top-level@^19.5.0":
+ version "19.5.0"
+ resolved "https://registry.yarnpkg.com/@commitlint/top-level/-/top-level-19.5.0.tgz#0017ffe39b5ba3611a1debd62efe28803601a14f"
+ integrity sha512-IP1YLmGAk0yWrImPRRc578I3dDUI5A2UBJx9FbSOjxe9sTlzFiwVJ+zeMLgAtHMtGZsC8LUnzmW1qRemkFU4ng==
+ dependencies:
+ find-up "^7.0.0"
+
+"@commitlint/types@^19.0.3", "@commitlint/types@^19.5.0":
+ version "19.5.0"
+ resolved "https://registry.yarnpkg.com/@commitlint/types/-/types-19.5.0.tgz#c5084d1231d4dd50e40bdb656ee7601f691400b3"
+ integrity sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg==
+ dependencies:
+ "@types/conventional-commits-parser" "^5.0.0"
+ chalk "^5.3.0"
+
+"@defi-wonderland/natspec-smells@1.1.3":
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/@defi-wonderland/natspec-smells/-/natspec-smells-1.1.3.tgz#6d4c7e289b24264856170fec33e0cae0c844bd32"
+ integrity sha512-QfZ7uD2bseU/QwQgY8uFrGSD5+a4y1S+GqCNePfmjgRGnEaV7zSFL5FO+zd1GUMtWQNGLgSuRknJMu6DEp3njw==
+ dependencies:
+ fast-glob "3.3.2"
+ solc-typed-ast "18.1.6"
+ yargs "17.7.2"
+
+"@noble/curves@1.4.2", "@noble/curves@~1.4.0":
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.2.tgz#40309198c76ed71bc6dbf7ba24e81ceb4d0d1fe9"
+ integrity sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==
+ dependencies:
+ "@noble/hashes" "1.4.0"
+
+"@noble/hashes@1.4.0", "@noble/hashes@~1.4.0":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426"
+ integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==
+
+"@nodelib/fs.scandir@2.1.5":
+ version "2.1.5"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
+ integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
+ dependencies:
+ "@nodelib/fs.stat" "2.0.5"
+ run-parallel "^1.1.9"
+
+"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
+ integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
+
+"@nodelib/fs.walk@^1.2.3":
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
+ integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
+ dependencies:
+ "@nodelib/fs.scandir" "2.1.5"
+ fastq "^1.6.0"
+
+"@openzeppelin/contracts-upgradeable@5.0.2":
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-5.0.2.tgz#3e5321a2ecdd0b206064356798c21225b6ec7105"
+ integrity sha512-0MmkHSHiW2NRFiT9/r5Lu4eJq5UJ4/tzlOgYXNAIj/ONkQTVnz22pLxDvp4C4uZ9he7ZFvGn3Driptn1/iU7tQ==
+
+"@openzeppelin/contracts@5.0.2":
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.0.2.tgz#b1d03075e49290d06570b2fd42154d76c2a5d210"
+ integrity sha512-ytPc6eLGcHHnapAZ9S+5qsdomhjo6QBHTDRRBFfTxXIpsicMhVPouPgmUPebZZZGX7vt9USA+Z+0M0dSVtSUEA==
+
+"@scure/base@~1.1.6":
+ version "1.1.9"
+ resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1"
+ integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==
+
+"@scure/bip32@1.4.0":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.4.0.tgz#4e1f1e196abedcef395b33b9674a042524e20d67"
+ integrity sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==
+ dependencies:
+ "@noble/curves" "~1.4.0"
+ "@noble/hashes" "~1.4.0"
+ "@scure/base" "~1.1.6"
+
+"@scure/bip39@1.3.0":
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.3.0.tgz#0f258c16823ddd00739461ac31398b4e7d6a18c3"
+ integrity sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==
+ dependencies:
+ "@noble/hashes" "~1.4.0"
+ "@scure/base" "~1.1.6"
+
+"@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"
+
+"@types/conventional-commits-parser@^5.0.0":
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz#8c9d23e0b415b24b91626d07017303755d542dc8"
+ integrity sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==
+ dependencies:
+ "@types/node" "*"
+
+"@types/node@*":
+ version "22.8.7"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-22.8.7.tgz#04ab7a073d95b4a6ee899f235d43f3c320a976f4"
+ integrity sha512-LidcG+2UeYIWcMuMUpBKOnryBWG/rnmOHQR5apjn8myTQcx3rinFRn7DcIFhMnS0PPFSC6OafdIKEad0lj6U0Q==
+ dependencies:
+ undici-types "~6.19.8"
+
+JSONStream@^1.3.5:
+ version "1.3.5"
+ resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0"
+ integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==
+ dependencies:
+ jsonparse "^1.2.0"
+ through ">=2.2.7 <3"
+
+abitype@0.7.1:
+ version "0.7.1"
+ resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.7.1.tgz#16db20abe67de80f6183cf75f3de1ff86453b745"
+ integrity sha512-VBkRHTDZf9Myaek/dO3yMmOzB/y2s3Zo6nVU7yaw1G+TvCHAjwaJzNGN9yo4K5D8bU/VZXKP1EJpRhFr862PlQ==
+
+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==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
+
+ajv@^8.0.1, ajv@^8.11.0:
+ version "8.17.1"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6"
+ integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==
+ dependencies:
+ fast-deep-equal "^3.1.3"
+ fast-uri "^3.0.1"
+ json-schema-traverse "^1.0.0"
+ require-from-string "^2.0.2"
+
+ansi-escapes@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-7.0.0.tgz#00fc19f491bbb18e1d481b97868204f92109bfe7"
+ integrity sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==
+ dependencies:
+ environment "^1.0.0"
+
+ansi-regex@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+ integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+ansi-regex@^6.0.1:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654"
+ integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==
+
+ansi-styles@^4.0.0, ansi-styles@^4.1.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+ integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+ dependencies:
+ color-convert "^2.0.1"
+
+ansi-styles@^6.0.0, ansi-styles@^6.2.1:
+ version "6.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
+ integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
+
+antlr4@^4.11.0:
+ version "4.13.2"
+ resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.13.2.tgz#0d084ad0e32620482a9c3a0e2470c02e72e4006d"
+ integrity sha512-QiVbZhyy4xAZ17UPEuG3YTOt8ZaoeOR1CvEAqrEsDBsOqINslaB147i9xqljZqoyf5S+EUlGStaj+t22LT9MOg==
+
+antlr4ts@^0.5.0-alpha.4:
+ version "0.5.0-alpha.4"
+ resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a"
+ integrity sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==
+
+argparse@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+ integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
+array-ify@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece"
+ integrity sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==
+
+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==
+
+astral-regex@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
+ integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
+
+asynckit@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+ integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
+
+available-typed-arrays@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846"
+ integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==
+ dependencies:
+ possible-typed-array-names "^1.0.0"
+
+axios@^1.6.8:
+ version "1.7.7"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f"
+ integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==
+ dependencies:
+ follow-redirects "^1.15.6"
+ form-data "^4.0.0"
+ proxy-from-env "^1.1.0"
+
+balanced-match@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+ integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+
+brace-expansion@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+ integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
+ dependencies:
+ balanced-match "^1.0.0"
+
+braces@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
+ integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
+ dependencies:
+ fill-range "^7.1.1"
+
+call-bind@^1.0.2, call-bind@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
+ integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==
+ dependencies:
+ es-define-property "^1.0.0"
+ es-errors "^1.3.0"
+ function-bind "^1.1.2"
+ get-intrinsic "^1.2.4"
+ set-function-length "^1.2.1"
+
+callsites@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+ integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+chalk@^4.1.2:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+ integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+ dependencies:
+ ansi-styles "^4.1.0"
+ supports-color "^7.1.0"
+
+chalk@^5.3.0, chalk@~5.3.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385"
+ integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==
+
+cli-cursor@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-5.0.0.tgz#24a4831ecf5a6b01ddeb32fb71a4b2088b0dce38"
+ integrity sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==
+ dependencies:
+ restore-cursor "^5.0.0"
+
+cli-truncate@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-4.0.0.tgz#6cc28a2924fee9e25ce91e973db56c7066e6172a"
+ integrity sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==
+ dependencies:
+ slice-ansi "^5.0.0"
+ string-width "^7.0.0"
+
+cliui@^8.0.1:
+ version "8.0.1"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
+ integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==
+ dependencies:
+ string-width "^4.2.0"
+ strip-ansi "^6.0.1"
+ wrap-ansi "^7.0.0"
+
+color-convert@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+ integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+ dependencies:
+ color-name "~1.1.4"
+
+color-name@~1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+ integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+colorette@^2.0.20:
+ version "2.0.20"
+ resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a"
+ integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==
+
+combined-stream@^1.0.8:
+ version "1.0.8"
+ resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+ integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+ dependencies:
+ delayed-stream "~1.0.0"
+
+command-exists@^1.2.8:
+ version "1.2.9"
+ resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69"
+ integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==
+
+commander@^11.1.0:
+ version "11.1.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-11.1.0.tgz#62fdce76006a68e5c1ab3314dc92e800eb83d906"
+ integrity sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==
+
+commander@^12.0.0, commander@~12.1.0:
+ version "12.1.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3"
+ integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==
+
+commander@^8.1.0:
+ version "8.3.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
+ integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
+
+compare-func@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3"
+ integrity sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==
+ dependencies:
+ array-ify "^1.0.0"
+ dot-prop "^5.1.0"
+
+conventional-changelog-angular@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz#5eec8edbff15aa9b1680a8dcfbd53e2d7eb2ba7a"
+ integrity sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==
+ dependencies:
+ compare-func "^2.0.0"
+
+conventional-changelog-conventionalcommits@^7.0.2:
+ version "7.0.2"
+ resolved "https://registry.yarnpkg.com/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz#aa5da0f1b2543094889e8cf7616ebe1a8f5c70d5"
+ integrity sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==
+ dependencies:
+ compare-func "^2.0.0"
+
+conventional-commits-parser@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz#57f3594b81ad54d40c1b4280f04554df28627d9a"
+ integrity sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==
+ dependencies:
+ JSONStream "^1.3.5"
+ is-text-path "^2.0.0"
+ meow "^12.0.1"
+ split2 "^4.0.0"
+
+cosmiconfig-typescript-loader@^5.0.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-5.1.0.tgz#d8d02bff04e63faa2dc794d618168bd764c704be"
+ integrity sha512-7PtBB+6FdsOvZyJtlF3hEPpACq7RQX6BVGsgC7/lfVXnKMvNCu/XY3ykreqG5w/rBNdu2z8LCIKoF3kpHHdHlA==
+ dependencies:
+ jiti "^1.21.6"
+
+cosmiconfig@^8.0.0:
+ version "8.3.6"
+ resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3"
+ integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==
+ dependencies:
+ import-fresh "^3.3.0"
+ js-yaml "^4.1.0"
+ parse-json "^5.2.0"
+ path-type "^4.0.0"
+
+cosmiconfig@^9.0.0:
+ version "9.0.0"
+ resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.0.tgz#34c3fc58287b915f3ae905ab6dc3de258b55ad9d"
+ integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==
+ dependencies:
+ env-paths "^2.2.1"
+ import-fresh "^3.3.0"
+ js-yaml "^4.1.0"
+ parse-json "^5.2.0"
+
+cross-spawn@^7.0.3:
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
+ integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
+ dependencies:
+ path-key "^3.1.0"
+ shebang-command "^2.0.0"
+ which "^2.0.1"
+
+dargs@^8.0.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/dargs/-/dargs-8.1.0.tgz#a34859ea509cbce45485e5aa356fef70bfcc7272"
+ integrity sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==
+
+debug@~4.3.6:
+ version "4.3.7"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
+ integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
+ dependencies:
+ ms "^2.1.3"
+
+decimal.js@^10.4.3:
+ version "10.4.3"
+ resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
+ integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
+
+define-data-property@^1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e"
+ integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==
+ dependencies:
+ es-define-property "^1.0.0"
+ es-errors "^1.3.0"
+ gopd "^1.0.1"
+
+delayed-stream@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+ integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+
+detect-file@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7"
+ integrity sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==
+
+detect-indent@^7.0.1:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-7.0.1.tgz#cbb060a12842b9c4d333f1cac4aa4da1bb66bc25"
+ integrity sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==
+
+detect-newline@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-4.0.1.tgz#fcefdb5713e1fb8cb2839b8b6ee22e6716ab8f23"
+ integrity sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==
+
+dir-glob@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
+ integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
+ dependencies:
+ path-type "^4.0.0"
+
+dot-prop@^5.1.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88"
+ integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==
+ dependencies:
+ is-obj "^2.0.0"
+
+emoji-regex@^10.3.0:
+ version "10.4.0"
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.4.0.tgz#03553afea80b3975749cfcb36f776ca268e413d4"
+ integrity sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==
+
+emoji-regex@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+ integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
+env-paths@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
+ integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
+
+environment@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/environment/-/environment-1.1.0.tgz#8e86c66b180f363c7ab311787e0259665f45a9f1"
+ integrity sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==
+
+error-ex@^1.3.1:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
+ integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
+ dependencies:
+ is-arrayish "^0.2.1"
+
+es-define-property@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845"
+ integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==
+ dependencies:
+ get-intrinsic "^1.2.4"
+
+es-errors@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
+ integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
+
+escalade@^3.1.1:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5"
+ integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
+
+ethereum-cryptography@^2.0.0:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz#58f2810f8e020aecb97de8c8c76147600b0b8ccf"
+ integrity sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==
+ dependencies:
+ "@noble/curves" "1.4.2"
+ "@noble/hashes" "1.4.0"
+ "@scure/bip32" "1.4.0"
+ "@scure/bip39" "1.3.0"
+
+eventemitter3@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4"
+ integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==
+
+execa@^8.0.1, execa@~8.0.1:
+ version "8.0.1"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c"
+ integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==
+ dependencies:
+ cross-spawn "^7.0.3"
+ get-stream "^8.0.1"
+ human-signals "^5.0.0"
+ is-stream "^3.0.0"
+ merge-stream "^2.0.0"
+ npm-run-path "^5.1.0"
+ onetime "^6.0.0"
+ signal-exit "^4.1.0"
+ strip-final-newline "^3.0.0"
+
+expand-tilde@^2.0.0, expand-tilde@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502"
+ integrity sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==
+ dependencies:
+ homedir-polyfill "^1.0.1"
+
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+ integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-diff@^1.2.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0"
+ integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==
+
+fast-glob@3.3.2, fast-glob@^3.3.0:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
+ integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
+ dependencies:
+ "@nodelib/fs.stat" "^2.0.2"
+ "@nodelib/fs.walk" "^1.2.3"
+ glob-parent "^5.1.2"
+ merge2 "^1.3.0"
+ micromatch "^4.0.4"
+
+fast-json-stable-stringify@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+ integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fast-uri@^3.0.1:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.3.tgz#892a1c91802d5d7860de728f18608a0573142241"
+ integrity sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==
+
+fastq@^1.6.0:
+ version "1.17.1"
+ resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47"
+ integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==
+ dependencies:
+ reusify "^1.0.4"
+
+fill-range@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
+ integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
+ dependencies:
+ to-regex-range "^5.0.1"
+
+find-up@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-7.0.0.tgz#e8dec1455f74f78d888ad65bf7ca13dd2b4e66fb"
+ integrity sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==
+ dependencies:
+ locate-path "^7.2.0"
+ path-exists "^5.0.0"
+ unicorn-magic "^0.1.0"
+
+findup-sync@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-5.0.0.tgz#54380ad965a7edca00cc8f63113559aadc541bd2"
+ integrity sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==
+ dependencies:
+ detect-file "^1.0.0"
+ is-glob "^4.0.3"
+ micromatch "^4.0.4"
+ resolve-dir "^1.0.1"
+
+follow-redirects@^1.12.1, follow-redirects@^1.15.6:
+ version "1.15.9"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1"
+ integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==
+
+for-each@^0.3.3:
+ version "0.3.3"
+ resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
+ integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==
+ dependencies:
+ is-callable "^1.1.3"
+
+"forge-std@github:foundry-rs/forge-std#1.9.2":
+ version "1.9.2"
+ resolved "https://codeload.github.com/foundry-rs/forge-std/tar.gz/1714bee72e286e73f76e320d110e0eaf5c4e649d"
+
+form-data@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48"
+ integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.8"
+ mime-types "^2.1.12"
+
+fs-extra@^11.2.0:
+ version "11.2.0"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b"
+ integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==
+ dependencies:
+ graceful-fs "^4.2.0"
+ jsonfile "^6.0.1"
+ universalify "^2.0.0"
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
+
+function-bind@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
+ integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
+
+get-caller-file@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
+ integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+
+get-east-asian-width@^1.0.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz#21b4071ee58ed04ee0db653371b55b4299875389"
+ integrity sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==
+
+get-intrinsic@^1.1.3, get-intrinsic@^1.2.4:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
+ integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
+ dependencies:
+ es-errors "^1.3.0"
+ function-bind "^1.1.2"
+ has-proto "^1.0.1"
+ has-symbols "^1.0.3"
+ hasown "^2.0.0"
+
+get-stdin@^9.0.0:
+ version "9.0.0"
+ resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-9.0.0.tgz#3983ff82e03d56f1b2ea0d3e60325f39d703a575"
+ integrity sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==
+
+get-stream@^8.0.1:
+ version "8.0.1"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2"
+ integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==
+
+git-hooks-list@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/git-hooks-list/-/git-hooks-list-3.1.0.tgz#386dc531dcc17474cf094743ff30987a3d3e70fc"
+ integrity sha512-LF8VeHeR7v+wAbXqfgRlTSX/1BJR9Q1vEMR8JAz1cEg6GX07+zyj3sAdDvYjj/xnlIfVuGgj4qBei1K3hKH+PA==
+
+git-raw-commits@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-4.0.0.tgz#b212fd2bff9726d27c1283a1157e829490593285"
+ integrity sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==
+ dependencies:
+ dargs "^8.0.0"
+ meow "^12.0.1"
+ split2 "^4.0.0"
+
+glob-parent@^5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+ integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
+ dependencies:
+ is-glob "^4.0.1"
+
+glob@^8.0.3:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e"
+ integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^5.0.1"
+ once "^1.3.0"
+
+global-directory@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/global-directory/-/global-directory-4.0.1.tgz#4d7ac7cfd2cb73f304c53b8810891748df5e361e"
+ integrity sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==
+ dependencies:
+ ini "4.1.1"
+
+global-modules@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea"
+ integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==
+ dependencies:
+ global-prefix "^1.0.1"
+ is-windows "^1.0.1"
+ resolve-dir "^1.0.0"
+
+global-prefix@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe"
+ integrity sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==
+ dependencies:
+ expand-tilde "^2.0.2"
+ homedir-polyfill "^1.0.1"
+ ini "^1.3.4"
+ is-windows "^1.0.1"
+ which "^1.2.14"
+
+globby@^13.1.2:
+ version "13.2.2"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592"
+ integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==
+ dependencies:
+ dir-glob "^3.0.1"
+ fast-glob "^3.3.0"
+ ignore "^5.2.4"
+ merge2 "^1.4.1"
+ slash "^4.0.0"
+
+gopd@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
+ integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
+ dependencies:
+ get-intrinsic "^1.1.3"
+
+graceful-fs@^4.1.6, graceful-fs@^4.2.0:
+ 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==
+
+has-flag@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+ integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+has-property-descriptors@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854"
+ integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==
+ dependencies:
+ es-define-property "^1.0.0"
+
+has-proto@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd"
+ integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==
+
+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-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"
+ integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==
+ dependencies:
+ has-symbols "^1.0.3"
+
+hasown@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
+ integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
+ dependencies:
+ function-bind "^1.1.2"
+
+homedir-polyfill@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8"
+ integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==
+ dependencies:
+ parse-passwd "^1.0.0"
+
+human-signals@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28"
+ integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==
+
+husky@>=9:
+ version "9.1.6"
+ resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.6.tgz#e23aa996b6203ab33534bdc82306b0cf2cb07d6c"
+ integrity sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A==
+
+ignore@^5.2.4:
+ version "5.3.2"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
+ integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
+
+import-fresh@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+ integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
+ dependencies:
+ parent-module "^1.0.0"
+ resolve-from "^4.0.0"
+
+import-meta-resolve@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz#f9db8bead9fafa61adb811db77a2bf22c5399706"
+ integrity sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2, inherits@^2.0.3:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+ini@4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/ini/-/ini-4.1.1.tgz#d95b3d843b1e906e56d6747d5447904ff50ce7a1"
+ integrity sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==
+
+ini@^1.3.4:
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
+ integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
+
+is-arguments@^1.0.4:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
+ integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==
+ dependencies:
+ call-bind "^1.0.2"
+ has-tostringtag "^1.0.0"
+
+is-arrayish@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+ integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
+
+is-callable@^1.1.3:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
+ integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
+
+is-extglob@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+ integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+
+is-fullwidth-code-point@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+ integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+
+is-fullwidth-code-point@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88"
+ integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==
+
+is-fullwidth-code-point@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz#9609efced7c2f97da7b60145ef481c787c7ba704"
+ integrity sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==
+ dependencies:
+ get-east-asian-width "^1.0.0"
+
+is-generator-function@^1.0.7:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72"
+ integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==
+ dependencies:
+ has-tostringtag "^1.0.0"
+
+is-glob@^4.0.1, is-glob@^4.0.3:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+ integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+ dependencies:
+ is-extglob "^2.1.1"
+
+is-number@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+ integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+is-obj@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982"
+ integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
+
+is-plain-obj@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0"
+ integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==
+
+is-stream@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac"
+ integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==
+
+is-text-path@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-2.0.0.tgz#b2484e2b720a633feb2e85b67dc193ff72c75636"
+ integrity sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==
+ dependencies:
+ text-extensions "^2.0.0"
+
+is-typed-array@^1.1.3:
+ version "1.1.13"
+ resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229"
+ integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==
+ dependencies:
+ which-typed-array "^1.1.14"
+
+is-windows@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
+ integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+ integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+
+jiti@^1.21.6:
+ version "1.21.6"
+ resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268"
+ integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==
+
+js-sha3@0.8.0:
+ version "0.8.0"
+ resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
+ integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==
+
+js-tokens@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+ integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+js-yaml@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+ integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+ dependencies:
+ argparse "^2.0.1"
+
+jsel@^1.1.6:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/jsel/-/jsel-1.1.6.tgz#9257fee6c6e8ad8e75d5706503fe84f376035896"
+ integrity sha512-7E6r8kVzjmKhwXR/82Z+43edfOJGRvLvx6cJZ+SS2MGAPPtYZGnaIsFHpQMA1IbIPA9twDProkob4IIAJ0ZqSw==
+
+json-parse-even-better-errors@^2.3.0:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
+ integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
+
+json-schema-traverse@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-schema-traverse@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
+ integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
+
+jsonfile@^6.0.1:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
+ integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
+ dependencies:
+ universalify "^2.0.0"
+ optionalDependencies:
+ graceful-fs "^4.1.6"
+
+jsonparse@^1.2.0:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
+ integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==
+
+lilconfig@~3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.2.tgz#e4a7c3cb549e3a606c8dcc32e5ae1005e62c05cb"
+ integrity sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==
+
+lines-and-columns@^1.1.6:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
+ integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
+
+lint-staged@>=10:
+ version "15.2.10"
+ resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-15.2.10.tgz#92ac222f802ba911897dcf23671da5bb80643cd2"
+ integrity sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==
+ dependencies:
+ chalk "~5.3.0"
+ commander "~12.1.0"
+ debug "~4.3.6"
+ execa "~8.0.1"
+ lilconfig "~3.1.2"
+ listr2 "~8.2.4"
+ micromatch "~4.0.8"
+ pidtree "~0.6.0"
+ string-argv "~0.3.2"
+ yaml "~2.5.0"
+
+listr2@~8.2.4:
+ version "8.2.5"
+ resolved "https://registry.yarnpkg.com/listr2/-/listr2-8.2.5.tgz#5c9db996e1afeb05db0448196d3d5f64fec2593d"
+ integrity sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==
+ dependencies:
+ cli-truncate "^4.0.0"
+ colorette "^2.0.20"
+ eventemitter3 "^5.0.1"
+ log-update "^6.1.0"
+ rfdc "^1.4.1"
+ wrap-ansi "^9.0.0"
+
+locate-path@^7.2.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-7.2.0.tgz#69cb1779bd90b35ab1e771e1f2f89a202c2a8a8a"
+ integrity sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==
+ dependencies:
+ p-locate "^6.0.0"
+
+lodash.camelcase@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
+ integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==
+
+lodash.isplainobject@^4.0.6:
+ version "4.0.6"
+ resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
+ integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==
+
+lodash.kebabcase@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36"
+ integrity sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==
+
+lodash.merge@^4.6.2:
+ version "4.6.2"
+ resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+ integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+
+lodash.mergewith@^4.6.2:
+ version "4.6.2"
+ resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55"
+ integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==
+
+lodash.snakecase@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d"
+ integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==
+
+lodash.startcase@^4.4.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8"
+ integrity sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==
+
+lodash.truncate@^4.4.2:
+ version "4.4.2"
+ resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
+ integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==
+
+lodash.uniq@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
+ integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==
+
+lodash.upperfirst@^4.3.1:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce"
+ integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==
+
+lodash@^4.17.21:
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+
+log-update@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/log-update/-/log-update-6.1.0.tgz#1a04ff38166f94647ae1af562f4bd6a15b1b7cd4"
+ integrity sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==
+ dependencies:
+ ansi-escapes "^7.0.0"
+ cli-cursor "^5.0.0"
+ slice-ansi "^7.1.0"
+ strip-ansi "^7.1.0"
+ wrap-ansi "^9.0.0"
+
+memorystream@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2"
+ integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==
+
+meow@^12.0.1:
+ version "12.1.1"
+ resolved "https://registry.yarnpkg.com/meow/-/meow-12.1.1.tgz#e558dddbab12477b69b2e9a2728c327f191bace6"
+ integrity sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==
+
+merge-stream@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
+ integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
+
+merge2@^1.3.0, merge2@^1.4.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
+ integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
+
+micromatch@^4.0.4, micromatch@~4.0.8:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
+ integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
+ dependencies:
+ braces "^3.0.3"
+ picomatch "^2.3.1"
+
+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-types@^2.1.12:
+ version "2.1.35"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+ integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+ dependencies:
+ mime-db "1.52.0"
+
+mimic-fn@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc"
+ integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==
+
+mimic-function@^5.0.0:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076"
+ integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==
+
+minimatch@^5.0.1:
+ version "5.1.6"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
+ integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
+ dependencies:
+ brace-expansion "^2.0.1"
+
+minimist@^1.2.8:
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
+ integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
+
+ms@^2.1.3:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+ integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
+npm-run-path@^5.1.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f"
+ integrity sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==
+ dependencies:
+ path-key "^4.0.0"
+
+once@^1.3.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
+ dependencies:
+ wrappy "1"
+
+onetime@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4"
+ integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==
+ dependencies:
+ mimic-fn "^4.0.0"
+
+onetime@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-7.0.0.tgz#9f16c92d8c9ef5120e3acd9dd9957cceecc1ab60"
+ integrity sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==
+ dependencies:
+ mimic-function "^5.0.0"
+
+optimism@ethereum-optimism/optimism#develop:
+ version "0.0.0"
+ resolved "https://codeload.github.com/ethereum-optimism/optimism/tar.gz/0c5617983d4ba1c8b083759749198e710271a09a"
+
+os-tmpdir@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+ integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==
+
+p-limit@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644"
+ integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==
+ dependencies:
+ yocto-queue "^1.0.0"
+
+p-locate@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-6.0.0.tgz#3da9a49d4934b901089dca3302fa65dc5a05c04f"
+ integrity sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==
+ dependencies:
+ p-limit "^4.0.0"
+
+parent-module@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+ integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+ dependencies:
+ callsites "^3.0.0"
+
+parse-json@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
+ integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
+ dependencies:
+ "@babel/code-frame" "^7.0.0"
+ error-ex "^1.3.1"
+ json-parse-even-better-errors "^2.3.0"
+ lines-and-columns "^1.1.6"
+
+parse-passwd@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
+ integrity sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==
+
+path-exists@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7"
+ integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==
+
+path-key@^3.1.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
+ integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+
+path-key@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18"
+ integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==
+
+path-type@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
+ integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
+
+picocolors@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
+ integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
+
+picomatch@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+ integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+pidtree@~0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c"
+ integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==
+
+pluralize@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1"
+ integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==
+
+possible-typed-array-names@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f"
+ integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==
+
+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==
+
+proxy-from-env@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
+ integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
+
+punycode@^2.1.0:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
+ integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
+
+queue-microtask@^1.2.2:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
+ integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
+
+require-directory@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+ integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
+
+require-from-string@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
+ integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
+
+resolve-dir@^1.0.0, resolve-dir@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43"
+ integrity sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==
+ dependencies:
+ expand-tilde "^2.0.0"
+ global-modules "^1.0.0"
+
+resolve-from@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+ integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+resolve-from@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
+ integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
+
+restore-cursor@^5.0.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-5.1.0.tgz#0766d95699efacb14150993f55baf0953ea1ebe7"
+ integrity sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==
+ dependencies:
+ onetime "^7.0.0"
+ signal-exit "^4.1.0"
+
+reusify@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
+ integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
+
+rfdc@^1.4.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca"
+ integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==
+
+run-parallel@^1.1.9:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
+ integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
+ dependencies:
+ queue-microtask "^1.2.2"
+
+semver@^5.5.0:
+ version "5.7.2"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
+ integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
+
+semver@^6.3.0:
+ version "6.3.1"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
+ integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
+
+semver@^7.6.0:
+ version "7.6.3"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
+ integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
+
+set-function-length@^1.2.1:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
+ integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==
+ dependencies:
+ define-data-property "^1.1.4"
+ es-errors "^1.3.0"
+ function-bind "^1.1.2"
+ get-intrinsic "^1.2.4"
+ gopd "^1.0.1"
+ has-property-descriptors "^1.0.2"
+
+shebang-command@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+ integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
+ dependencies:
+ shebang-regex "^3.0.0"
+
+shebang-regex@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
+ integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
+
+signal-exit@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
+ integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
+
+slash@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7"
+ integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==
+
+slice-ansi@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b"
+ integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==
+ dependencies:
+ ansi-styles "^4.0.0"
+ astral-regex "^2.0.0"
+ is-fullwidth-code-point "^3.0.0"
+
+slice-ansi@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a"
+ integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==
+ dependencies:
+ ansi-styles "^6.0.0"
+ is-fullwidth-code-point "^4.0.0"
+
+slice-ansi@^7.1.0:
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-7.1.0.tgz#cd6b4655e298a8d1bdeb04250a433094b347b9a9"
+ integrity sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==
+ dependencies:
+ ansi-styles "^6.2.1"
+ is-fullwidth-code-point "^5.0.0"
+
+solc-typed-ast@18.1.6:
+ version "18.1.6"
+ resolved "https://registry.yarnpkg.com/solc-typed-ast/-/solc-typed-ast-18.1.6.tgz#997e986583f0fdddb3ddb1960c33d5c63b3f729a"
+ integrity sha512-nBk24fdju+P2xsy32tG6HLqkXI+Tn+W84Fqm5+XD1Xby2/8YLlsMgI3ADoRPhhO7DeWjq/kflm//dGNkEb3ILA==
+ dependencies:
+ axios "^1.6.8"
+ commander "^12.0.0"
+ decimal.js "^10.4.3"
+ findup-sync "^5.0.0"
+ fs-extra "^11.2.0"
+ jsel "^1.1.6"
+ semver "^7.6.0"
+ solc "0.8.25"
+ src-location "^1.1.0"
+ web3-eth-abi "^4.2.0"
+
+solc@0.8.25:
+ version "0.8.25"
+ resolved "https://registry.yarnpkg.com/solc/-/solc-0.8.25.tgz#393f3101617388fb4ba2a58c5b03ab02678e375c"
+ integrity sha512-7P0TF8gPeudl1Ko3RGkyY6XVCxe2SdD/qQhtns1vl3yAbK/PDifKDLHGtx1t7mX3LgR7ojV7Fg/Kc6Q9D2T8UQ==
+ dependencies:
+ command-exists "^1.2.8"
+ commander "^8.1.0"
+ follow-redirects "^1.12.1"
+ js-sha3 "0.8.0"
+ memorystream "^0.3.1"
+ semver "^5.5.0"
+ tmp "0.0.33"
+
+solhint-community@4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/solhint-community/-/solhint-community-4.0.0.tgz#4dba66932ff54ced426a8c035b7ceaa13a224f24"
+ integrity sha512-BERw3qYzkJE64EwvYrp2+iiTN8yAZOJ74FCiL4bTBp7v0JFUvRYCEGZKAqfHcfi/koKkzM6qThsJUceKm9vvfg==
+ 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 "^11.1.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 "^6.3.0"
+ strip-ansi "^6.0.1"
+ table "^6.8.1"
+ text-table "^0.2.0"
+ optionalDependencies:
+ prettier "^2.8.3"
+
+sort-object-keys@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/sort-object-keys/-/sort-object-keys-1.1.3.tgz#bff833fe85cab147b34742e45863453c1e190b45"
+ integrity sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==
+
+sort-package-json@2.10.0:
+ version "2.10.0"
+ resolved "https://registry.yarnpkg.com/sort-package-json/-/sort-package-json-2.10.0.tgz#6be07424bf3b7db9fbb1bdd69e7945f301026d8a"
+ integrity sha512-MYecfvObMwJjjJskhxYfuOADkXp1ZMMnCFC8yhp+9HDsk7HhR336hd7eiBs96lTXfiqmUNI+WQCeCMRBhl251g==
+ dependencies:
+ detect-indent "^7.0.1"
+ detect-newline "^4.0.0"
+ get-stdin "^9.0.0"
+ git-hooks-list "^3.0.0"
+ globby "^13.1.2"
+ is-plain-obj "^4.1.0"
+ semver "^7.6.0"
+ sort-object-keys "^1.1.3"
+
+split2@^4.0.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4"
+ integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==
+
+src-location@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/src-location/-/src-location-1.1.0.tgz#3f50eeb0c7169432e55b426be6e3a68ebba16218"
+ integrity sha512-idBVZgLZGzB3B2Et317AFDQto7yRgp1tOuFd+VKIH2dw1jO1b6p07zNjtQoVhkW+CY6oGTp9Y5UIfbJoZRsoFQ==
+
+string-argv@~0.3.2:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6"
+ integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==
+
+string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+string-width@^7.0.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.2.0.tgz#b5bb8e2165ce275d4d43476dd2700ad9091db6dc"
+ integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==
+ dependencies:
+ emoji-regex "^10.3.0"
+ get-east-asian-width "^1.0.0"
+ strip-ansi "^7.1.0"
+
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+strip-ansi@^7.1.0:
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
+ integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==
+ dependencies:
+ ansi-regex "^6.0.1"
+
+strip-final-newline@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd"
+ integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==
+
+supports-color@^7.1.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+ integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+ dependencies:
+ has-flag "^4.0.0"
+
+table@^6.8.1:
+ version "6.8.2"
+ resolved "https://registry.yarnpkg.com/table/-/table-6.8.2.tgz#c5504ccf201213fa227248bdc8c5569716ac6c58"
+ integrity sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==
+ dependencies:
+ ajv "^8.0.1"
+ lodash.truncate "^4.4.2"
+ slice-ansi "^4.0.0"
+ string-width "^4.2.3"
+ strip-ansi "^6.0.1"
+
+text-extensions@^2.0.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-2.4.0.tgz#a1cfcc50cf34da41bfd047cc744f804d1680ea34"
+ integrity sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==
+
+text-table@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+ integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
+
+"through@>=2.2.7 <3":
+ version "2.3.8"
+ resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+ integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
+
+tinyexec@^0.3.0:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.1.tgz#0ab0daf93b43e2c211212396bdb836b468c97c98"
+ integrity sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==
+
+tmp@0.0.33:
+ version "0.0.33"
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
+ integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
+ dependencies:
+ os-tmpdir "~1.0.2"
+
+to-regex-range@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+ integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+ dependencies:
+ is-number "^7.0.0"
+
+undici-types@~6.19.8:
+ version "6.19.8"
+ resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
+ integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
+
+unicorn-magic@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4"
+ integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==
+
+universalify@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
+ integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
+
+uri-js@^4.2.2:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+ integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+ dependencies:
+ punycode "^2.1.0"
+
+util@^0.12.5:
+ version "0.12.5"
+ resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc"
+ integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==
+ dependencies:
+ inherits "^2.0.3"
+ is-arguments "^1.0.4"
+ is-generator-function "^1.0.7"
+ is-typed-array "^1.1.3"
+ which-typed-array "^1.1.2"
+
+web3-errors@^1.2.0, web3-errors@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/web3-errors/-/web3-errors-1.3.0.tgz#504e4d3218899df108856940087a8022d6688d74"
+ integrity sha512-j5JkAKCtuVMbY3F5PYXBqg1vWrtF4jcyyMY1rlw8a4PV67AkqlepjGgpzWJZd56Mt+TvHy6DA1F/3Id8LatDSQ==
+ dependencies:
+ web3-types "^1.7.0"
+
+web3-eth-abi@^4.2.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-4.3.0.tgz#9036541206418b9db6f1db295ad87a6f0fa0ed7d"
+ integrity sha512-OqZPGGxHmfKJt33BfpclEMmWvnnLJ/B+jVTnVagd2OIU1kIv09xf/E60ei0eGeG612uFy/pPq31u4RidF/gf6g==
+ dependencies:
+ abitype "0.7.1"
+ web3-errors "^1.3.0"
+ web3-types "^1.8.1"
+ web3-utils "^4.3.2"
+ web3-validator "^2.0.6"
+
+web3-types@^1.6.0, web3-types@^1.7.0, web3-types@^1.8.1:
+ version "1.8.1"
+ resolved "https://registry.yarnpkg.com/web3-types/-/web3-types-1.8.1.tgz#6379aca0f99330eb0f8f6ca80a3de93129b58339"
+ integrity sha512-isspsvQbBJFUkJYz2Badb7dz/BrLLLpOop/WmnL5InyYMr7kYYc8038NYO7Vkp1M7Bupa/wg+yALvBm7EGbyoQ==
+
+web3-utils@^4.3.2:
+ version "4.3.2"
+ resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-4.3.2.tgz#a952428d677b635fd0c16044ae4c534807a39639"
+ integrity sha512-bEFpYEBMf6ER78Uvj2mdsCbaLGLK9kABOsa3TtXOEEhuaMy/RK0KlRkKoZ2vmf/p3hB8e1q5erknZ6Hy7AVp7A==
+ dependencies:
+ ethereum-cryptography "^2.0.0"
+ eventemitter3 "^5.0.1"
+ web3-errors "^1.3.0"
+ web3-types "^1.8.1"
+ web3-validator "^2.0.6"
+
+web3-validator@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/web3-validator/-/web3-validator-2.0.6.tgz#a0cdaa39e1d1708ece5fae155b034e29d6a19248"
+ integrity sha512-qn9id0/l1bWmvH4XfnG/JtGKKwut2Vokl6YXP5Kfg424npysmtRLe9DgiNBM9Op7QL/aSiaA0TVXibuIuWcizg==
+ dependencies:
+ ethereum-cryptography "^2.0.0"
+ util "^0.12.5"
+ web3-errors "^1.2.0"
+ web3-types "^1.6.0"
+ zod "^3.21.4"
+
+which-typed-array@^1.1.14, which-typed-array@^1.1.2:
+ version "1.1.15"
+ resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d"
+ integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==
+ dependencies:
+ available-typed-arrays "^1.0.7"
+ call-bind "^1.0.7"
+ for-each "^0.3.3"
+ gopd "^1.0.1"
+ has-tostringtag "^1.0.2"
+
+which@^1.2.14:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
+ integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
+ dependencies:
+ isexe "^2.0.0"
+
+which@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+ integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+ dependencies:
+ isexe "^2.0.0"
+
+wrap-ansi@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
+wrap-ansi@^9.0.0:
+ version "9.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-9.0.0.tgz#1a3dc8b70d85eeb8398ddfb1e4a02cd186e58b3e"
+ integrity sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==
+ dependencies:
+ ansi-styles "^6.2.1"
+ string-width "^7.0.0"
+ strip-ansi "^7.1.0"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
+
+y18n@^5.0.5:
+ version "5.0.8"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
+ integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
+
+yaml@~2.5.0:
+ version "2.5.1"
+ resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.5.1.tgz#c9772aacf62cb7494a95b0c4f1fb065b563db130"
+ integrity sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==
+
+yargs-parser@^21.1.1:
+ version "21.1.1"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
+ integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
+
+yargs@17.7.2, yargs@^17.0.0:
+ version "17.7.2"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
+ integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==
+ dependencies:
+ cliui "^8.0.1"
+ escalade "^3.1.1"
+ get-caller-file "^2.0.5"
+ require-directory "^2.1.1"
+ string-width "^4.2.3"
+ y18n "^5.0.5"
+ yargs-parser "^21.1.1"
+
+yocto-queue@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.1.1.tgz#fef65ce3ac9f8a32ceac5a634f74e17e5b232110"
+ integrity sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==
+
+zod@^3.21.4:
+ version "3.23.8"
+ resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"
+ integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==