diff --git a/.changeset/early-rules-yell.md b/.changeset/early-rules-yell.md new file mode 100644 index 00000000000..718f48063e8 --- /dev/null +++ b/.changeset/early-rules-yell.md @@ -0,0 +1,5 @@ +--- +"chainlink": minor +--- + +#updated support aptos creation in chain config UI diff --git a/.changeset/five-chicken-talk.md b/.changeset/five-chicken-talk.md new file mode 100644 index 00000000000..5f93b29364b --- /dev/null +++ b/.changeset/five-chicken-talk.md @@ -0,0 +1,5 @@ +--- +"chainlink": minor +--- + +#updated introduce network field on chain resolver diff --git a/.changeset/forty-lizards-camp.md b/.changeset/forty-lizards-camp.md new file mode 100644 index 00000000000..3e0a5a59b0e --- /dev/null +++ b/.changeset/forty-lizards-camp.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +Enable rotating encryptionPublicKey in CapabilitiesRegistry contract diff --git a/.changeset/metal-eels-check.md b/.changeset/metal-eels-check.md new file mode 100644 index 00000000000..387eb51fc6d --- /dev/null +++ b/.changeset/metal-eels-check.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +#updated Consume Feeds Manager WSRPC protos from Chainlink Protos Repository. diff --git a/.changeset/orange-feet-share.md b/.changeset/orange-feet-share.md new file mode 100644 index 00000000000..9583bce3d64 --- /dev/null +++ b/.changeset/orange-feet-share.md @@ -0,0 +1,25 @@ +--- +"chainlink": minor +--- + +Implemented new chain agnostic MultiNode design along with the corresponding EVM implementation. The chain agnostic components enable Multinode to be integrated with Solana and other non-EVM chains. Previously the Multinode was coupled with EVM specific actions, and was called to execute these actions direclty. With this change, the MultiNode's responsibility has been simplified to focus on RPC selection along with performing health checks. Chain specific actions will instead be executed on the RPC directly after being selected by MultiNode. The Chain Agnostic MultiNode provides improved reliability and metrics for all chain integrations using it. + +These are following main components: +Node: Common component which wraps an RPC with state information, health checks, and an alive loop to handle state changes along with maintaining chain information. +RPCClient: Chain-specific RPC wrapper which implements required interface for MultiNode along with any chain-specific functionality needed. +MultiNode: Perform RPCClient selection and performs health checks on all RPCs. +TransactionSender: Chain agnostic component which broadcasts transactions to all healthy RPCs and aggregates results. A chain-specific error classifier must be implemented. + +MultiNode picks the "best" RPC based on one of the configurable criteria: +- Priority defined in the config. +- Highest latest block. +- Round-robin within the same priority level (or using other configurable selection algorithms) + +Benefits of Chain Agnostic MultiNode: + Reliability: Improved RPC reliability scaleable to all chains + Maintainability: Can apply changes across all chain integrations through the use of common code + Extendability: Can add new health checks, RPC selection and ranking algorithms + Integration Speed: Much faster to integrate MultiNode with new chains + Reduced Generics: Significantly less bulky code! + +#updated #changed #internal diff --git a/.changeset/stale-pugs-sin.md b/.changeset/stale-pugs-sin.md new file mode 100644 index 00000000000..58ebd2d7acd --- /dev/null +++ b/.changeset/stale-pugs-sin.md @@ -0,0 +1,5 @@ +--- +"chainlink": minor +--- + +#added introduce cosmosKeys and starknetKeys graphql query diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 400a19eceac..60b94f802a8 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -58,24 +58,26 @@ core/scripts/gateway @smartcontractkit/dev-services /core/services/standardcapabilities @smartcontractkit/keystone /core/scripts/keystone @smartcontractkit/keystone -# Contracts +# Contracts catch all, for files not matched by the more specific patterns below /contracts/ @RensR @matYang @RayXpub @elatoskinas -# First we match on project names to catch files like the compilation scripts, -# gas snapshots and other files not places in the project directories. -# This could give some false positives, so afterwards we match on the project directories -# to ensure the entire directory is always owned by the correct team. - +# First we match on project names to catch files like the compilation scripts and other files +# not placed in the project directories. This could give some false positives, so afterwards +# we match on the project directories to ensure the entire directory is always owned by the +# correct team. /contracts/**/*keeper* @smartcontractkit/dev-services /contracts/**/*upkeep* @smartcontractkit/dev-services /contracts/**/*automation* @smartcontractkit/dev-services +/contracts/**/*ccip* @RensR @matYang @jhweintraub @0xsuryansh @RyanRHall /contracts/**/*functions* @smartcontractkit/dev-services +/contracts/**/*l2ep* @smartcontractkit/bix-ship /contracts/**/*llo-feeds* @smartcontractkit/data-streams-engineers +/contracts/**/*operatorforwarder* @smartcontractkit/data-feeds-engineers /contracts/**/*vrf* @smartcontractkit/dev-services -/contracts/**/*l2ep* @smartcontractkit/bix-ship /contracts/**/*keystone* @smartcontractkit/keystone /contracts/src/v0.8/automation @smartcontractkit/dev-services +/contracts/src/v0.8/ccip @RensR @matYang @jhweintraub @0xsuryansh @RyanRHall /contracts/src/v0.8/functions @smartcontractkit/dev-services # TODO: interfaces folder, folder should be removed and files moved to the correct folders /contracts/src/v0.8/l2ep @smartcontractkit/bix-build @@ -86,15 +88,35 @@ core/scripts/gateway @smartcontractkit/dev-services # TODO: tests folder, folder should be removed and files moved to the correct folders # TODO: transmission folder, owner should be found /contracts/src/v0.8/vrf @smartcontractkit/dev-services +/contracts/src/v0.8/keystone @smartcontractkit/keystone + +/core/gethwrappers/ccip @RensR @matYang @jhweintraub @0xsuryansh @RyanRHall +/core/gethwrappers/functions @smartcontractkit/dev-services +/core/gethwrappers/keystone @smartcontractkit/keystone +/core/gethwrappers/liquiditymanager @RensR @matYang @jhweintraub @0xsuryansh @RyanRHall +/core/gethwrappers/llo-feeds @smartcontractkit/data-streams-engineers +/core/gethwrappers/operatorforwarder @smartcontractkit/data-feeds-engineers +/core/gethwrappers/shared @RensR @matYang @RayXpub @elatoskinas + +# The following don't exist yet but should. They are already included here to allow the teams to +# set these folders up and own them immediately. +/core/gethwrappers/keeper @smartcontractkit/dev-services +/core/gethwrappers/upkeep @smartcontractkit/dev-services +/core/gethwrappers/automation @smartcontractkit/dev-services +/core/gethwrappers/l2ep @smartcontractkit/bix-ship +/core/gethwrappers/vrf @smartcontractkit/dev-services -# GQL API -/core/web/resolver @smartcontractkit/deployment-automation @smartcontractkit/foundations -/core/web/schema @smartcontractkit/deployment-automation @smartcontractkit/foundations +# Remove changeset files from the codeowners +/contracts/.changeset +# Gas snapshots are always checked by the CI so they don't need codeowners. +/contracts/gas-snapshots # At the end, match any files missed by the patterns above /contracts/scripts/native_solc_compile_all_events_mock @smartcontractkit/dev-services -# Remove changeset files from the codeowners -/contracts/.changeset + +# GQL API +/core/web/resolver @smartcontractkit/deployment-automation @smartcontractkit/foundations +/core/web/schema @smartcontractkit/deployment-automation @smartcontractkit/foundations # Tests diff --git a/.github/workflows/solidity-foundry.yml b/.github/workflows/solidity-foundry.yml index e2e9855c25d..9380eb6f039 100644 --- a/.github/workflows/solidity-foundry.yml +++ b/.github/workflows/solidity-foundry.yml @@ -252,279 +252,6 @@ jobs: artifact-name: code-coverage-report-${{ matrix.product.name }} working-directory: ./contracts - # runs only if non-test contracts were modified; scoped only to modified or added contracts - analyze: - needs: [ changes, define-matrix ] - name: Run static analysis - if: needs.changes.outputs.not_test_sol_modified == 'true' - runs-on: ubuntu-22.04 - steps: - - name: Checkout this repository - uses: actions/checkout@v4.2.1 - - - name: Checkout .github repository - uses: actions/checkout@v4.2.1 - with: - repository: smartcontractkit/.github - ref: b6e37806737eef87e8c9137ceeb23ef0bff8b1db # validate-solidity-artifacts@0.1.0 - path: ./dot_github - - - name: Setup NodeJS - uses: ./.github/actions/setup-nodejs - - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@8f1998e9878d786675189ef566a2e4bf24869773 # v1.2.0 - with: - version: ${{ needs.define-matrix.outputs.foundry-version }} - - - name: Set up Python - uses: actions/setup-python@v5.2.0 - with: - python-version: '3.8' - - - name: Install solc-select and solc - uses: smartcontractkit/.github/actions/setup-solc-select@b6e37806737eef87e8c9137ceeb23ef0bff8b1db # validate-solidity-artifacts@0.1.0 - with: - to_install: '0.8.24' - to_use: '0.8.24' - - - name: Install Slither - uses: smartcontractkit/.github/actions/setup-slither@b6e37806737eef87e8c9137ceeb23ef0bff8b1db # validate-solidity-artifacts@0.1.0 - - - name: Run Slither - shell: bash - run: | - # modify remappings so that solc can find dependencies - ./dot_github/tools/scripts/solidity/modify_remappings.sh contracts contracts/remappings.txt - mv remappings_modified.txt remappings.txt - - # without it Slither sometimes fails to use remappings correctly - cp contracts/foundry.toml foundry.toml - - FILES="${{ needs.changes.outputs.not_test_sol_modified_files }}" - - for FILE in $FILES; do - PRODUCT=$(echo "$FILE" | awk -F'src/[^/]*/' '{print $2}' | cut -d'/' -f1) - echo "::debug::Running Slither for $FILE in $PRODUCT" - SLITHER_CONFIG="contracts/configs/slither/.slither.config-$PRODUCT-pr.json" - if [[ ! -f $SLITHER_CONFIG ]]; then - echo "::debug::No Slither config found for $PRODUCT, using default" - SLITHER_CONFIG="contracts/configs/slither/.slither.config-default-pr.json" - fi - ./dot_github/tools/scripts/solidity/generate_slither_report.sh "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.sha }}/" "$SLITHER_CONFIG" "./contracts" "$FILE" "contracts/slither-reports-current" "--solc-remaps @=contracts/node_modules/@" - done - - # all the actions below, up to printing results, run only if any existing contracts were modified - # in that case we extract new issues introduced by the changes by using an LLM model - - name: Upload Slither results for current branch - if: needs.changes.outputs.sol_mod_only == 'true' - uses: actions/upload-artifact@v4.4.3 - timeout-minutes: 2 - continue-on-error: true - with: - name: slither-reports-current-${{ github.sha }} - path: contracts/slither-reports-current - retention-days: 7 - - # we need to upload scripts and configuration in case base_ref doesn't have the scripts, or they are in different version - - name: Upload Slither scripts - if: needs.changes.outputs.sol_mod_only == 'true' - uses: actions/upload-artifact@v4.4.3 - timeout-minutes: 2 - continue-on-error: true - with: - name: tmp-slither-scripts-${{ github.sha }} - path: ./dot_github/tools/scripts/solidity - retention-days: 7 - - - name: Upload configs - if: needs.changes.outputs.sol_mod_only == 'true' - uses: actions/upload-artifact@v4.4.3 - timeout-minutes: 2 - continue-on-error: true - with: - name: tmp-configs-${{ github.sha }} - path: contracts/configs - retention-days: 7 - - - name: Checkout earlier version of this repository - if: needs.changes.outputs.sol_mod_only == 'true' - uses: actions/checkout@v4.2.1 - with: - ref: ${{ github.base_ref }} - - - name: Download Slither scripts - if: needs.changes.outputs.sol_mod_only == 'true' - uses: actions/download-artifact@v4.1.8 - with: - name: tmp-slither-scripts-${{ github.sha }} - path: ./dot_github/tools/scripts/solidity - - - name: Download configs - if: needs.changes.outputs.sol_mod_only == 'true' - uses: actions/download-artifact@v4.1.8 - with: - name: tmp-configs-${{ github.sha }} - path: contracts/configs - - # since we have just checked out the repository again, we lose NPM dependencies installs previously, we need to install them again to compile contracts - - name: Setup NodeJS - if: needs.changes.outputs.sol_mod_only == 'true' - uses: ./.github/actions/setup-nodejs - - - name: Run Slither for base reference - if: needs.changes.outputs.sol_mod_only == 'true' - shell: bash - run: | - # we need to set file permission again since they are lost during download - for file in ./dot_github/tools/scripts/solidity/*.sh; do - chmod +x "$file" - done - - # modify remappings so that solc can find dependencies - ./dot_github/tools/scripts/solidity/modify_remappings.sh contracts contracts/remappings.txt - mv remappings_modified.txt remappings.txt - - # without it Slither sometimes fails to use remappings correctly - cp contracts/foundry.toml foundry.toml - - FILES="${{ needs.changes.outputs.sol_mod_only_files }}" - - for FILE in $FILES; do - PRODUCT=$(echo "$FILE" | awk -F'src/[^/]*/' '{print $2}' | cut -d'/' -f1) - echo "::debug::Running Slither for $FILE in $PRODUCT" - SLITHER_CONFIG="contracts/configs/slither/.slither.config-$PRODUCT-pr.json" - if [[ ! -f $SLITHER_CONFIG ]]; then - echo "::debug::No Slither config found for $PRODUCT, using default" - SLITHER_CONFIG="contracts/configs/slither/.slither.config-default-pr.json" - fi - ./dot_github/tools/scripts/solidity/generate_slither_report.sh "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.sha }}/" "$SLITHER_CONFIG" "./contracts" "$FILE" "contracts/slither-reports-base-ref" "--solc-remaps @=contracts/node_modules/@" - done - - - name: Upload Slither report - if: needs.changes.outputs.sol_mod_only == 'true' - uses: actions/upload-artifact@v4.4.3 - timeout-minutes: 10 - continue-on-error: true - with: - name: slither-reports-base-${{ github.sha }} - path: | - contracts/slither-reports-base-ref - retention-days: 7 - - - name: Download Slither results for current branch - if: needs.changes.outputs.sol_mod_only == 'true' - uses: actions/download-artifact@v4.1.8 - with: - name: slither-reports-current-${{ github.sha }} - path: contracts/slither-reports-current - - - name: Generate diff of Slither reports for modified files - if: needs.changes.outputs.sol_mod_only == 'true' - env: - OPEN_API_KEY: ${{ secrets.OPEN_AI_SLITHER_API_KEY }} - shell: bash - run: | - set -euo pipefail - for base_report in contracts/slither-reports-base-ref/*.md; do - filename=$(basename "$base_report") - current_report="contracts/slither-reports-current/$filename" - new_issues_report="contracts/slither-reports-current/${filename%.md}_new_issues.md" - if [ -f "$current_report" ]; then - if ./contracts/scripts/ci/find_slither_report_diff.sh "$base_report" "$current_report" "$new_issues_report" "contracts/scripts/ci/prompt-difference.md" "contracts/scripts/ci/prompt-validation.md"; then - if [[ -s $new_issues_report ]]; then - awk 'NR==2{print "*This new issues report has been automatically generated by LLM model using two Slither reports. One based on `${{ github.base_ref}}` and another on `${{ github.sha }}` commits.*"}1' $new_issues_report > tmp.md && mv tmp.md $new_issues_report - echo "Replacing full Slither report with diff for $current_report" - rm $current_report && mv $new_issues_report $current_report - else - echo "No difference detected between $base_report and $current_report reports. Won't include any of them." - rm $current_report - fi - else - echo "::warning::Failed to generate a diff report with new issues for $base_report using an LLM model, will use full report." - fi - - else - echo "::warning::Failed to find current commit's equivalent of $base_report (file $current_report doesn't exist, but should have been generated). Please check Slither logs." - fi - done - - # actions that execute only if any existing contracts were modified end here - - name: Print Slither summary - shell: bash - run: | - echo "# Static analysis results " >> $GITHUB_STEP_SUMMARY - for file in "contracts/slither-reports-current"/*.md; do - if [ -e "$file" ]; then - cat "$file" >> $GITHUB_STEP_SUMMARY - fi - done - - - name: Validate if all Slither run for all contracts - uses: smartcontractkit/.github/actions/validate-solidity-artifacts@094e8de69ca35d17f321cecc062cbeed12642ef5 # validate-solidity-artifacts@0.2.0 - with: - validate_slither_reports: 'true' - validate_uml_diagrams: 'false' - slither_reports_path: 'contracts/slither-reports-current' - sol_files: ${{ needs.changes.outputs.not_test_sol_modified_files }} - - - name: Upload Slither reports - uses: actions/upload-artifact@v4.4.3 - timeout-minutes: 10 - continue-on-error: true - with: - name: slither-reports-${{ github.sha }} - path: | - contracts/slither-reports-current - retention-days: 7 - - - name: Find Slither comment in the PR - # We only want to create the comment if the PR is not modified by a bot - if: "(github.event_name == 'push' && github.event.pusher.username && ! contains(github.event.pusher.username, '[bot]')) || (github.event_name != 'push' && ! contains(github.actor, '[bot]'))" - uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.0.0 - id: find-comment - with: - issue-number: ${{ github.event.pull_request.number }} - comment-author: 'github-actions[bot]' - body-includes: 'Static analysis results' - - - name: Extract job summary URL - id: job-summary-url - uses: pl-strflt/job-summary-url-action@df2d22c5351f73e0a187d20879854b8d98e6e001 # v1.0.0 - with: - job: 'Run static analysis' - - - name: Build Slither reports artifacts URL - id: build-slither-artifact-url - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - ARTIFACTS=$(gh api -X GET repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts) - ARTIFACT_ID=$(echo "$ARTIFACTS" | jq '.artifacts[] | select(.name=="slither-reports-${{ github.sha }}") | .id') - echo "Artifact ID: $ARTIFACT_ID" - - slither_artifact_url="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/$ARTIFACT_ID" - echo "slither_artifact_url=$slither_artifact_url" >> $GITHUB_OUTPUT - - - name: Create or update Slither comment in the PR - # We only want to create the comment if the PR is not modified by a bot - if: "(github.event_name == 'push' && github.event.pusher.username && ! contains(github.event.pusher.username, '[bot]')) || (github.event_name != 'push' && ! contains(github.actor, '[bot]'))" - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 - with: - comment-id: ${{ steps.find-comment.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - body: | - ## Static analysis results are available - Hey @${{ github.event.push && github.event.push.pusher && github.event.push.pusher.username || github.actor }}, you can view Slither reports in the job summary [here](${{ steps.job-summary-url.outputs.job_summary_url }}) or download them as artifact [here](${{ steps.build-slither-artifact-url.outputs.slither_artifact_url }}). - - Please check them before merging and make sure you have addressed all issues. - edit-mode: replace - - - name: Remove temp artifacts - uses: geekyeggo/delete-artifact@24928e75e6e6590170563b8ddae9fac674508aa1 # v5.0 - with: - name: tmp-* - solidity-forge-fmt: name: Forge fmt ${{ matrix.product.name }} if: ${{ needs.changes.outputs.non_src_changes == 'true' || needs.changes.outputs.not_test_sol_modified == 'true' }} diff --git a/.goreleaser.develop.yaml b/.goreleaser.develop.yaml index d461c4402f2..1e054df7c45 100644 --- a/.goreleaser.develop.yaml +++ b/.goreleaser.develop.yaml @@ -3,7 +3,6 @@ project_name: chainlink env: - IMG_PRE={{ if index .Env "IMAGE_PREFIX" }}{{ .Env.IMAGE_PREFIX }}{{ else }}localhost:5001{{ end }} - IMG_TAG={{ if index .Env "IMAGE_TAG" }}{{ .Env.IMAGE_TAG }}{{ else }}develop{{ end }} - - CGO_ENABLED=1 - VERSION={{ if index .Env "CHAINLINK_VERSION" }}{{ .Env.CHAINLINK_VERSION }}{{ else }}v0.0.0-local{{ end }} release: disable: "true" @@ -17,9 +16,8 @@ builds: no_unique_dist_dir: "true" ldflags: - -s -w -r=$ORIGIN/libs - - -X github.com/smartcontractkit/chainlink/v2/core/static.Sha={{ .FullCommit }} - - -extldflags "-Wl,--dynamic-linker=/lib{{ if contains .Runtime.Goarch "amd64" }}64{{end}}/ld-linux-{{ if contains .Runtime.Goarch "arm64" }}aarch64{{ else }}x86-64{{end}}.so.1" - -X github.com/smartcontractkit/chainlink/v2/core/static.Version={{ .Env.VERSION }} + - -X github.com/smartcontractkit/chainlink/v2/core/static.Sha={{ .FullCommit }} flags: - -trimpath - -buildmode=pie @@ -51,8 +49,8 @@ dockers: - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.source=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.title=chainlink - - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.version={{ .Env.VERSION }} + - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink use: buildx - id: linux-amd64-chainlink-plugins goos: linux @@ -80,8 +78,8 @@ dockers: - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.source=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.title=chainlink - - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.version={{ .Env.VERSION }} + - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink use: buildx - id: linux-arm64-chainlink goos: linux @@ -103,8 +101,8 @@ dockers: - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.source=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.title=chainlink - - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.version={{ .Env.VERSION }} + - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink use: buildx - id: linux-arm64-chainlink-plugins goos: linux @@ -131,8 +129,8 @@ dockers: - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.source=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.title=chainlink - - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.version={{ .Env.VERSION }} + - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink use: buildx - id: linux-amd64-ccip goos: linux @@ -157,8 +155,8 @@ dockers: - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.source=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.title=chainlink - - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.version={{ .Env.VERSION }} + - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink use: buildx - id: linux-amd64-ccip-plugins goos: linux @@ -188,8 +186,8 @@ dockers: - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.source=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.title=chainlink - - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.version={{ .Env.VERSION }} + - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink use: buildx - id: linux-arm64-ccip goos: linux @@ -213,8 +211,8 @@ dockers: - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.source=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.title=chainlink - - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.version={{ .Env.VERSION }} + - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink use: buildx - id: linux-arm64-ccip-plugins goos: linux @@ -243,8 +241,8 @@ dockers: - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.source=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.title=chainlink - - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.version={{ .Env.VERSION }} + - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink use: buildx docker_manifests: - id: tagged-chainlink diff --git a/.goreleaser.devspace.yaml b/.goreleaser.devspace.yaml index 816f2cb878e..d0167b34cda 100644 --- a/.goreleaser.devspace.yaml +++ b/.goreleaser.devspace.yaml @@ -1,97 +1,89 @@ +project_name: chainlink-devspace + version: 2 -project_name: chainlink + env: - - IMG_PRE={{ if index .Env "IMAGE_PREFIX" }}{{ .Env.IMAGE_PREFIX }}{{ else }}localhost:5001{{ end }} - - IMG_TAG={{ if index .Env "IMAGE_TAG" }}{{ .Env.IMAGE_TAG }}{{ else }}develop{{ end }} - - CGO_ENABLED=1 -release: - disable: "true" + - ZIG_EXEC={{ if index .Env "ZIG_EXEC" }}{{ .Env.ZIG_EXEC }}{{ else }}zig{{ end }} + - IMAGE_LABEL_DESCRIPTION="node of the decentralized oracle network, bridging on and off-chain computation" + - IMAGE_LABEL_LICENSES="MIT" + - IMAGE_LABEL_SOURCE="https://github.com/smartcontractkit/{{ .ProjectName }}" + +before: + hooks: + - go mod tidy + - ./tools/bin/goreleaser_utils before_hook + +# See https://goreleaser.com/customization/build/ builds: - - targets: - - go_first_class - binary: chainlink - hooks: - post: - - cmd: ./tools/bin/goreleaser_utils build_post_hook {{ dir .Path }} - no_unique_dist_dir: "true" - ldflags: - - -s -w -r=$ORIGIN/libs - - -X github.com/smartcontractkit/chainlink/v2/core/static.Sha={{ .FullCommit }} - - -extldflags "-Wl,--dynamic-linker=/lib{{ if contains .Runtime.Goarch "amd64" }}64{{end}}/ld-linux-{{ if contains .Runtime.Goarch "arm64" }}aarch64{{ else }}x86-64{{end}}.so.1" - - -X github.com/smartcontractkit/chainlink/v2/core/static.Version={{ .Version }} - flags: - - -trimpath - - -buildmode=pie -archives: - - format: binary -snapshot: - version_template: v0.0.0-{{ .Runtime.Goarch }}-{{ .Now.Format "2006-01-02-15-04-05Z" }} -checksum: - name_template: checksums.txt + - binary: chainlink + id: linux-amd64 + goos: + - linux + goarch: + - amd64 + hooks: + post: ./tools/bin/goreleaser_utils build_post_hook {{ dir .Path }} {{ .Os }} {{ .Arch }} + env: + - CGO_ENABLED=1 + - CC=$ZIG_EXEC cc -target x86_64-linux-gnu -Wno-error=unused-command-line-argument + - CCX=$ZIG_EXEC c++ -target x86_64-linux-gnu -Wno-error=unused-command-line-argument + flags: + - -trimpath + - -buildmode=pie + ldflags: + - -s -w -r=$ORIGIN/libs + - -X github.com/smartcontractkit/chainlink/v2/core/static.Version={{ .Version }} + - -X github.com/smartcontractkit/chainlink/v2/core/static.Sha={{ .FullCommit }} + +# See https://goreleaser.com/customization/docker/ dockers: - - id: linux-amd64 - goos: linux - goarch: amd64 - dockerfile: core/chainlink.goreleaser.Dockerfile - image_templates: - - '{{ .Env.IMAGE }}' - extra_files: - - tmp/libs - - tmp/plugins - build_flag_templates: - - --platform=linux/amd64 - - --pull - - --build-arg=CHAINLINK_USER=chainlink - - --build-arg=COMMIT_SHA={{ .FullCommit }} - - --build-arg=CL_MEDIAN_CMD=chainlink-feeds - - --build-arg=CL_MERCURY_CMD=chainlink-mercury - - --build-arg=CL_SOLANA_CMD=chainlink-solana - - --build-arg=CL_STARKNET_CMD=chainlink-starknet - - --label=org.opencontainers.image.created={{ .Date }} - - --label=org.opencontainers.image.description="node of the decentralized oracle network, bridging on and off-chain computation" - - --label=org.opencontainers.image.licenses=MIT - - --label=org.opencontainers.image.revision={{ .FullCommit }} - - --label=org.opencontainers.image.source=https://github.com/smartcontractkit/chainlink - - --label=org.opencontainers.image.title=chainlink - - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink - use: buildx - - id: linux-arm64 - goos: linux - goarch: arm64 - dockerfile: core/chainlink.goreleaser.Dockerfile - image_templates: - - '{{ .Env.IMAGE }}' - extra_files: - - tmp/libs - - tmp/plugins - build_flag_templates: - - --platform=linux/arm64 - - --pull - - --build-arg=CHAINLINK_USER=chainlink - - --build-arg=COMMIT_SHA={{ .FullCommit }} - - --build-arg=CL_MEDIAN_CMD=chainlink-feeds - - --build-arg=CL_MERCURY_CMD=chainlink-mercury - - --build-arg=CL_SOLANA_CMD=chainlink-solana - - --build-arg=CL_STARKNET_CMD=chainlink-starknet - - --label=org.opencontainers.image.created={{ .Date }} - - --label=org.opencontainers.image.description="node of the decentralized oracle network, bridging on and off-chain computation" - - --label=org.opencontainers.image.licenses=MIT - - --label=org.opencontainers.image.revision={{ .FullCommit }} - - --label=org.opencontainers.image.source=https://github.com/smartcontractkit/chainlink - - --label=org.opencontainers.image.title=chainlink - - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink - use: buildx + - id: linux-amd64 + dockerfile: core/chainlink.goreleaser.Dockerfile + use: buildx + goos: linux + goarch: amd64 + extra_files: + - tmp/linux_amd64/libs + - tmp/linux_amd64/plugins + - tools/bin/ldd_fix + build_flag_templates: + - "--platform=linux/amd64" + - "--pull" + - "--build-arg=CHAINLINK_USER=chainlink" + - "--build-arg=COMMIT_SHA={{ .FullCommit }}" + - "--build-arg=CL_MEDIAN_CMD=chainlink-feeds" + - "--build-arg=CL_MERCURY_CMD=chainlink-mercury" + - "--build-arg=CL_SOLANA_CMD=chainlink-solana" + - "--build-arg=CL_STARKNET_CMD=chainlink-starknet" + - "--label=org.opencontainers.image.created={{ .Date }}" + - "--label=org.opencontainers.image.description={{ .Env.IMAGE_LABEL_DESCRIPTION }}" + - "--label=org.opencontainers.image.licenses={{ .Env.IMAGE_LABEL_LICENSES }}" + - "--label=org.opencontainers.image.revision={{ .FullCommit }}" + - "--label=org.opencontainers.image.source={{ .Env.IMAGE_LABEL_SOURCE }}" + - "--label=org.opencontainers.image.title={{ .ProjectName }}" + - "--label=org.opencontainers.image.version={{ .Version }}" + - "--label=org.opencontainers.image.url={{ .Env.IMAGE_LABEL_SOURCE }}" + image_templates: + - "{{ .Env.IMAGE }}" + +# See https://goreleaser.com/customization/docker_manifest/ docker_manifests: - - name_template: '{{ .Env.IMAGE }}' - image_templates: - - '{{ .Env.IMAGE }}' + - name_template: "{{ .Env.IMAGE }}" + image_templates: + - "{{ .Env.IMAGE }}" + +checksum: + name_template: "checksums.txt" + +snapshot: + version_template: '{{ .Env.CHAINLINK_VERSION }}-{{ .Runtime.Goarch }}-{{ .Now.Format "2006-01-02-15-04-05Z" }}' + changelog: - disable: "true" -before: - hooks: - - cmd: go mod tidy - - cmd: ./tools/bin/goreleaser_utils before_hook -partial: - by: target -nightly: - version_template: v0.0.0-{{ .Runtime.Goarch }}-{{ .Now.Format "2006-01-02-15-04-05Z" }} + sort: asc + filters: + exclude: + - "^docs:" + - "^test:" +# modelines, feel free to remove those if you don't want/use them: +# yaml-language-server: $schema=https://goreleaser.com/static/schema.json +# vim: set ts=2 sw=2 tw=0 fo=cnqoj diff --git a/.goreleaser.production.yaml b/.goreleaser.production.yaml index b29130bc54d..ada9b847e74 100644 --- a/.goreleaser.production.yaml +++ b/.goreleaser.production.yaml @@ -3,7 +3,6 @@ project_name: chainlink env: - IMG_PRE={{ if index .Env "IMAGE_PREFIX" }}{{ .Env.IMAGE_PREFIX }}{{ else }}localhost:5001{{ end }} - IMG_TAG={{ if index .Env "IMAGE_TAG" }}{{ .Env.IMAGE_TAG }}{{ else }}develop{{ end }} - - CGO_ENABLED=1 - VERSION={{ if index .Env "CHAINLINK_VERSION" }}{{ .Env.CHAINLINK_VERSION }}{{ else }}v0.0.0-local{{ end }} release: disable: "true" @@ -17,9 +16,8 @@ builds: no_unique_dist_dir: "true" ldflags: - -s -w -r=$ORIGIN/libs - - -X github.com/smartcontractkit/chainlink/v2/core/static.Sha={{ .FullCommit }} - - -extldflags "-Wl,--dynamic-linker=/lib{{ if contains .Runtime.Goarch "amd64" }}64{{end}}/ld-linux-{{ if contains .Runtime.Goarch "arm64" }}aarch64{{ else }}x86-64{{end}}.so.1" - -X github.com/smartcontractkit/chainlink/v2/core/static.Version={{ .Env.VERSION }} + - -X github.com/smartcontractkit/chainlink/v2/core/static.Sha={{ .FullCommit }} flags: - -trimpath - -buildmode=pie @@ -52,8 +50,8 @@ dockers: - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.source=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.title=chainlink - - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.version={{ .Env.VERSION }} + - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink use: buildx - id: linux-amd64-chainlink-plugins goos: linux @@ -82,8 +80,8 @@ dockers: - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.source=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.title=chainlink - - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.version={{ .Env.VERSION }} + - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink use: buildx - id: linux-arm64-chainlink goos: linux @@ -106,8 +104,8 @@ dockers: - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.source=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.title=chainlink - - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.version={{ .Env.VERSION }} + - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink use: buildx - id: linux-arm64-chainlink-plugins goos: linux @@ -135,8 +133,8 @@ dockers: - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.source=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.title=chainlink - - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.version={{ .Env.VERSION }} + - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink use: buildx - id: linux-amd64-ccip goos: linux @@ -162,8 +160,8 @@ dockers: - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.source=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.title=chainlink - - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.version={{ .Env.VERSION }} + - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink use: buildx - id: linux-amd64-ccip-plugins goos: linux @@ -194,8 +192,8 @@ dockers: - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.source=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.title=chainlink - - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.version={{ .Env.VERSION }} + - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink use: buildx - id: linux-arm64-ccip goos: linux @@ -220,8 +218,8 @@ dockers: - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.source=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.title=chainlink - - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.version={{ .Env.VERSION }} + - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink use: buildx - id: linux-arm64-ccip-plugins goos: linux @@ -251,8 +249,8 @@ dockers: - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.source=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.title=chainlink - - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink - --label=org.opencontainers.image.version={{ .Env.VERSION }} + - --label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink use: buildx docker_manifests: - id: tagged-chainlink-chainlink-experimental-goreleaser diff --git a/.mockery.yaml b/.mockery.yaml index 709134b05bd..b74fedf2158 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -14,9 +14,8 @@ packages: NodeSelector: sendOnlyClient: SendOnlyNode: - RPC: + RPCClient: Head: - NodeClient: PoolChainInfoProvider: github.com/smartcontractkit/chainlink/v2/common/headtracker: interfaces: @@ -54,7 +53,6 @@ packages: github.com/smartcontractkit/chainlink/v2/core/chains/evm/client: interfaces: Client: - RPCClient: github.com/smartcontractkit/chainlink/v2/core/chains/evm/config: interfaces: GasEstimator: @@ -170,9 +168,9 @@ packages: ConnectionsManager: ORM: Service: - github.com/smartcontractkit/chainlink/v2/core/services/feeds/proto: + github.com/smartcontractkit/chainlink-protos/orchestrator/feedsmanager: config: - dir: "{{ .InterfaceDir }}/../mocks" + dir: "core/services/feeds/mocks" interfaces: FeedsManagerClient: github.com/smartcontractkit/chainlink/v2/core/services/fluxmonitorv2: diff --git a/common/client/mock_node_client_test.go b/common/client/mock_node_client_test.go deleted file mode 100644 index 5643dcde90e..00000000000 --- a/common/client/mock_node_client_test.go +++ /dev/null @@ -1,718 +0,0 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. - -package client - -import ( - context "context" - - types "github.com/smartcontractkit/chainlink/v2/common/types" - mock "github.com/stretchr/testify/mock" -) - -// mockNodeClient is an autogenerated mock type for the NodeClient type -type mockNodeClient[CHAIN_ID types.ID, HEAD Head] struct { - mock.Mock -} - -type mockNodeClient_Expecter[CHAIN_ID types.ID, HEAD Head] struct { - mock *mock.Mock -} - -func (_m *mockNodeClient[CHAIN_ID, HEAD]) EXPECT() *mockNodeClient_Expecter[CHAIN_ID, HEAD] { - return &mockNodeClient_Expecter[CHAIN_ID, HEAD]{mock: &_m.Mock} -} - -// ChainID provides a mock function with given fields: ctx -func (_m *mockNodeClient[CHAIN_ID, HEAD]) ChainID(ctx context.Context) (CHAIN_ID, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for ChainID") - } - - var r0 CHAIN_ID - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (CHAIN_ID, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) CHAIN_ID); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(CHAIN_ID) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockNodeClient_ChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChainID' -type mockNodeClient_ChainID_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// ChainID is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) ChainID(ctx interface{}) *mockNodeClient_ChainID_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_ChainID_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("ChainID", ctx)} -} - -func (_c *mockNodeClient_ChainID_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockNodeClient_ChainID_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockNodeClient_ChainID_Call[CHAIN_ID, HEAD]) Return(_a0 CHAIN_ID, _a1 error) *mockNodeClient_ChainID_Call[CHAIN_ID, HEAD] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockNodeClient_ChainID_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (CHAIN_ID, error)) *mockNodeClient_ChainID_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// ClientVersion provides a mock function with given fields: _a0 -func (_m *mockNodeClient[CHAIN_ID, HEAD]) ClientVersion(_a0 context.Context) (string, error) { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for ClientVersion") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (string, error)); ok { - return rf(_a0) - } - if rf, ok := ret.Get(0).(func(context.Context) string); ok { - r0 = rf(_a0) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(_a0) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockNodeClient_ClientVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClientVersion' -type mockNodeClient_ClientVersion_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// ClientVersion is a helper method to define mock.On call -// - _a0 context.Context -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) ClientVersion(_a0 interface{}) *mockNodeClient_ClientVersion_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_ClientVersion_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("ClientVersion", _a0)} -} - -func (_c *mockNodeClient_ClientVersion_Call[CHAIN_ID, HEAD]) Run(run func(_a0 context.Context)) *mockNodeClient_ClientVersion_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockNodeClient_ClientVersion_Call[CHAIN_ID, HEAD]) Return(_a0 string, _a1 error) *mockNodeClient_ClientVersion_Call[CHAIN_ID, HEAD] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockNodeClient_ClientVersion_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (string, error)) *mockNodeClient_ClientVersion_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// Close provides a mock function with given fields: -func (_m *mockNodeClient[CHAIN_ID, HEAD]) Close() { - _m.Called() -} - -// mockNodeClient_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type mockNodeClient_Close_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// Close is a helper method to define mock.On call -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) Close() *mockNodeClient_Close_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_Close_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("Close")} -} - -func (_c *mockNodeClient_Close_Call[CHAIN_ID, HEAD]) Run(run func()) *mockNodeClient_Close_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNodeClient_Close_Call[CHAIN_ID, HEAD]) Return() *mockNodeClient_Close_Call[CHAIN_ID, HEAD] { - _c.Call.Return() - return _c -} - -func (_c *mockNodeClient_Close_Call[CHAIN_ID, HEAD]) RunAndReturn(run func()) *mockNodeClient_Close_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// Dial provides a mock function with given fields: ctx -func (_m *mockNodeClient[CHAIN_ID, HEAD]) Dial(ctx context.Context) error { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for Dial") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = rf(ctx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockNodeClient_Dial_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Dial' -type mockNodeClient_Dial_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// Dial is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) Dial(ctx interface{}) *mockNodeClient_Dial_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_Dial_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("Dial", ctx)} -} - -func (_c *mockNodeClient_Dial_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockNodeClient_Dial_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockNodeClient_Dial_Call[CHAIN_ID, HEAD]) Return(_a0 error) *mockNodeClient_Dial_Call[CHAIN_ID, HEAD] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockNodeClient_Dial_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) error) *mockNodeClient_Dial_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// DialHTTP provides a mock function with given fields: -func (_m *mockNodeClient[CHAIN_ID, HEAD]) DialHTTP() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for DialHTTP") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockNodeClient_DialHTTP_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DialHTTP' -type mockNodeClient_DialHTTP_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// DialHTTP is a helper method to define mock.On call -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) DialHTTP() *mockNodeClient_DialHTTP_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_DialHTTP_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("DialHTTP")} -} - -func (_c *mockNodeClient_DialHTTP_Call[CHAIN_ID, HEAD]) Run(run func()) *mockNodeClient_DialHTTP_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNodeClient_DialHTTP_Call[CHAIN_ID, HEAD]) Return(_a0 error) *mockNodeClient_DialHTTP_Call[CHAIN_ID, HEAD] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockNodeClient_DialHTTP_Call[CHAIN_ID, HEAD]) RunAndReturn(run func() error) *mockNodeClient_DialHTTP_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// DisconnectAll provides a mock function with given fields: -func (_m *mockNodeClient[CHAIN_ID, HEAD]) DisconnectAll() { - _m.Called() -} - -// mockNodeClient_DisconnectAll_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectAll' -type mockNodeClient_DisconnectAll_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// DisconnectAll is a helper method to define mock.On call -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) DisconnectAll() *mockNodeClient_DisconnectAll_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_DisconnectAll_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("DisconnectAll")} -} - -func (_c *mockNodeClient_DisconnectAll_Call[CHAIN_ID, HEAD]) Run(run func()) *mockNodeClient_DisconnectAll_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNodeClient_DisconnectAll_Call[CHAIN_ID, HEAD]) Return() *mockNodeClient_DisconnectAll_Call[CHAIN_ID, HEAD] { - _c.Call.Return() - return _c -} - -func (_c *mockNodeClient_DisconnectAll_Call[CHAIN_ID, HEAD]) RunAndReturn(run func()) *mockNodeClient_DisconnectAll_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// GetInterceptedChainInfo provides a mock function with given fields: -func (_m *mockNodeClient[CHAIN_ID, HEAD]) GetInterceptedChainInfo() (ChainInfo, ChainInfo) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetInterceptedChainInfo") - } - - var r0 ChainInfo - var r1 ChainInfo - if rf, ok := ret.Get(0).(func() (ChainInfo, ChainInfo)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() ChainInfo); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(ChainInfo) - } - - if rf, ok := ret.Get(1).(func() ChainInfo); ok { - r1 = rf() - } else { - r1 = ret.Get(1).(ChainInfo) - } - - return r0, r1 -} - -// mockNodeClient_GetInterceptedChainInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetInterceptedChainInfo' -type mockNodeClient_GetInterceptedChainInfo_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// GetInterceptedChainInfo is a helper method to define mock.On call -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) GetInterceptedChainInfo() *mockNodeClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("GetInterceptedChainInfo")} -} - -func (_c *mockNodeClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]) Run(run func()) *mockNodeClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNodeClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]) Return(latest ChainInfo, highestUserObservations ChainInfo) *mockNodeClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] { - _c.Call.Return(latest, highestUserObservations) - return _c -} - -func (_c *mockNodeClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]) RunAndReturn(run func() (ChainInfo, ChainInfo)) *mockNodeClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// IsSyncing provides a mock function with given fields: ctx -func (_m *mockNodeClient[CHAIN_ID, HEAD]) IsSyncing(ctx context.Context) (bool, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for IsSyncing") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (bool, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) bool); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockNodeClient_IsSyncing_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsSyncing' -type mockNodeClient_IsSyncing_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// IsSyncing is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) IsSyncing(ctx interface{}) *mockNodeClient_IsSyncing_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_IsSyncing_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("IsSyncing", ctx)} -} - -func (_c *mockNodeClient_IsSyncing_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockNodeClient_IsSyncing_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockNodeClient_IsSyncing_Call[CHAIN_ID, HEAD]) Return(_a0 bool, _a1 error) *mockNodeClient_IsSyncing_Call[CHAIN_ID, HEAD] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockNodeClient_IsSyncing_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (bool, error)) *mockNodeClient_IsSyncing_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// SetAliveLoopSub provides a mock function with given fields: _a0 -func (_m *mockNodeClient[CHAIN_ID, HEAD]) SetAliveLoopSub(_a0 types.Subscription) { - _m.Called(_a0) -} - -// mockNodeClient_SetAliveLoopSub_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetAliveLoopSub' -type mockNodeClient_SetAliveLoopSub_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// SetAliveLoopSub is a helper method to define mock.On call -// - _a0 types.Subscription -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) SetAliveLoopSub(_a0 interface{}) *mockNodeClient_SetAliveLoopSub_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_SetAliveLoopSub_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("SetAliveLoopSub", _a0)} -} - -func (_c *mockNodeClient_SetAliveLoopSub_Call[CHAIN_ID, HEAD]) Run(run func(_a0 types.Subscription)) *mockNodeClient_SetAliveLoopSub_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(types.Subscription)) - }) - return _c -} - -func (_c *mockNodeClient_SetAliveLoopSub_Call[CHAIN_ID, HEAD]) Return() *mockNodeClient_SetAliveLoopSub_Call[CHAIN_ID, HEAD] { - _c.Call.Return() - return _c -} - -func (_c *mockNodeClient_SetAliveLoopSub_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(types.Subscription)) *mockNodeClient_SetAliveLoopSub_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// SubscribeNewHead provides a mock function with given fields: ctx, channel -func (_m *mockNodeClient[CHAIN_ID, HEAD]) SubscribeNewHead(ctx context.Context, channel chan<- HEAD) (types.Subscription, error) { - ret := _m.Called(ctx, channel) - - if len(ret) == 0 { - panic("no return value specified for SubscribeNewHead") - } - - var r0 types.Subscription - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD) (types.Subscription, error)); ok { - return rf(ctx, channel) - } - if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD) types.Subscription); ok { - r0 = rf(ctx, channel) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(types.Subscription) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, chan<- HEAD) error); ok { - r1 = rf(ctx, channel) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockNodeClient_SubscribeNewHead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeNewHead' -type mockNodeClient_SubscribeNewHead_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// SubscribeNewHead is a helper method to define mock.On call -// - ctx context.Context -// - channel chan<- HEAD -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) SubscribeNewHead(ctx interface{}, channel interface{}) *mockNodeClient_SubscribeNewHead_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_SubscribeNewHead_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("SubscribeNewHead", ctx, channel)} -} - -func (_c *mockNodeClient_SubscribeNewHead_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context, channel chan<- HEAD)) *mockNodeClient_SubscribeNewHead_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(chan<- HEAD)) - }) - return _c -} - -func (_c *mockNodeClient_SubscribeNewHead_Call[CHAIN_ID, HEAD]) Return(s types.Subscription, err error) *mockNodeClient_SubscribeNewHead_Call[CHAIN_ID, HEAD] { - _c.Call.Return(s, err) - return _c -} - -func (_c *mockNodeClient_SubscribeNewHead_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context, chan<- HEAD) (types.Subscription, error)) *mockNodeClient_SubscribeNewHead_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// SubscribeToFinalizedHeads provides a mock function with given fields: _a0 -func (_m *mockNodeClient[CHAIN_ID, HEAD]) SubscribeToFinalizedHeads(_a0 context.Context) (<-chan HEAD, types.Subscription, error) { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for SubscribeToFinalizedHeads") - } - - var r0 <-chan HEAD - var r1 types.Subscription - var r2 error - if rf, ok := ret.Get(0).(func(context.Context) (<-chan HEAD, types.Subscription, error)); ok { - return rf(_a0) - } - if rf, ok := ret.Get(0).(func(context.Context) <-chan HEAD); ok { - r0 = rf(_a0) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan HEAD) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) types.Subscription); ok { - r1 = rf(_a0) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(types.Subscription) - } - } - - if rf, ok := ret.Get(2).(func(context.Context) error); ok { - r2 = rf(_a0) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// mockNodeClient_SubscribeToFinalizedHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToFinalizedHeads' -type mockNodeClient_SubscribeToFinalizedHeads_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// SubscribeToFinalizedHeads is a helper method to define mock.On call -// - _a0 context.Context -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) SubscribeToFinalizedHeads(_a0 interface{}) *mockNodeClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("SubscribeToFinalizedHeads", _a0)} -} - -func (_c *mockNodeClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]) Run(run func(_a0 context.Context)) *mockNodeClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockNodeClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]) Return(_a0 <-chan HEAD, _a1 types.Subscription, _a2 error) *mockNodeClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] { - _c.Call.Return(_a0, _a1, _a2) - return _c -} - -func (_c *mockNodeClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (<-chan HEAD, types.Subscription, error)) *mockNodeClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// SubscribeToHeads provides a mock function with given fields: ctx -func (_m *mockNodeClient[CHAIN_ID, HEAD]) SubscribeToHeads(ctx context.Context) (<-chan HEAD, types.Subscription, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for SubscribeToHeads") - } - - var r0 <-chan HEAD - var r1 types.Subscription - var r2 error - if rf, ok := ret.Get(0).(func(context.Context) (<-chan HEAD, types.Subscription, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) <-chan HEAD); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan HEAD) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) types.Subscription); ok { - r1 = rf(ctx) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(types.Subscription) - } - } - - if rf, ok := ret.Get(2).(func(context.Context) error); ok { - r2 = rf(ctx) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// mockNodeClient_SubscribeToHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToHeads' -type mockNodeClient_SubscribeToHeads_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// SubscribeToHeads is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) SubscribeToHeads(ctx interface{}) *mockNodeClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("SubscribeToHeads", ctx)} -} - -func (_c *mockNodeClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockNodeClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockNodeClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]) Return(ch <-chan HEAD, sub types.Subscription, err error) *mockNodeClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] { - _c.Call.Return(ch, sub, err) - return _c -} - -func (_c *mockNodeClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (<-chan HEAD, types.Subscription, error)) *mockNodeClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// SubscribersCount provides a mock function with given fields: -func (_m *mockNodeClient[CHAIN_ID, HEAD]) SubscribersCount() int32 { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for SubscribersCount") - } - - var r0 int32 - if rf, ok := ret.Get(0).(func() int32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int32) - } - - return r0 -} - -// mockNodeClient_SubscribersCount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribersCount' -type mockNodeClient_SubscribersCount_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// SubscribersCount is a helper method to define mock.On call -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) SubscribersCount() *mockNodeClient_SubscribersCount_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_SubscribersCount_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("SubscribersCount")} -} - -func (_c *mockNodeClient_SubscribersCount_Call[CHAIN_ID, HEAD]) Run(run func()) *mockNodeClient_SubscribersCount_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNodeClient_SubscribersCount_Call[CHAIN_ID, HEAD]) Return(_a0 int32) *mockNodeClient_SubscribersCount_Call[CHAIN_ID, HEAD] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockNodeClient_SubscribersCount_Call[CHAIN_ID, HEAD]) RunAndReturn(run func() int32) *mockNodeClient_SubscribersCount_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// UnsubscribeAllExceptAliveLoop provides a mock function with given fields: -func (_m *mockNodeClient[CHAIN_ID, HEAD]) UnsubscribeAllExceptAliveLoop() { - _m.Called() -} - -// mockNodeClient_UnsubscribeAllExceptAliveLoop_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnsubscribeAllExceptAliveLoop' -type mockNodeClient_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID types.ID, HEAD Head] struct { - *mock.Call -} - -// UnsubscribeAllExceptAliveLoop is a helper method to define mock.On call -func (_e *mockNodeClient_Expecter[CHAIN_ID, HEAD]) UnsubscribeAllExceptAliveLoop() *mockNodeClient_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD] { - return &mockNodeClient_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("UnsubscribeAllExceptAliveLoop")} -} - -func (_c *mockNodeClient_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD]) Run(run func()) *mockNodeClient_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNodeClient_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD]) Return() *mockNodeClient_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD] { - _c.Call.Return() - return _c -} - -func (_c *mockNodeClient_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD]) RunAndReturn(run func()) *mockNodeClient_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD] { - _c.Call.Return(run) - return _c -} - -// newMockNodeClient creates a new instance of mockNodeClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func newMockNodeClient[CHAIN_ID types.ID, HEAD Head](t interface { - mock.TestingT - Cleanup(func()) -}) *mockNodeClient[CHAIN_ID, HEAD] { - mock := &mockNodeClient[CHAIN_ID, HEAD]{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/common/client/mock_node_selector_test.go b/common/client/mock_node_selector_test.go index d05c2979fc1..2860f8076fd 100644 --- a/common/client/mock_node_selector_test.go +++ b/common/client/mock_node_selector_test.go @@ -8,20 +8,20 @@ import ( ) // mockNodeSelector is an autogenerated mock type for the NodeSelector type -type mockNodeSelector[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNodeSelector[CHAIN_ID types.ID, RPC interface{}] struct { mock.Mock } -type mockNodeSelector_Expecter[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNodeSelector_Expecter[CHAIN_ID types.ID, RPC interface{}] struct { mock *mock.Mock } -func (_m *mockNodeSelector[CHAIN_ID, HEAD, RPC]) EXPECT() *mockNodeSelector_Expecter[CHAIN_ID, HEAD, RPC] { - return &mockNodeSelector_Expecter[CHAIN_ID, HEAD, RPC]{mock: &_m.Mock} +func (_m *mockNodeSelector[CHAIN_ID, RPC]) EXPECT() *mockNodeSelector_Expecter[CHAIN_ID, RPC] { + return &mockNodeSelector_Expecter[CHAIN_ID, RPC]{mock: &_m.Mock} } // Name provides a mock function with given fields: -func (_m *mockNodeSelector[CHAIN_ID, HEAD, RPC]) Name() string { +func (_m *mockNodeSelector[CHAIN_ID, RPC]) Name() string { ret := _m.Called() if len(ret) == 0 { @@ -39,46 +39,46 @@ func (_m *mockNodeSelector[CHAIN_ID, HEAD, RPC]) Name() string { } // mockNodeSelector_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' -type mockNodeSelector_Name_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNodeSelector_Name_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // Name is a helper method to define mock.On call -func (_e *mockNodeSelector_Expecter[CHAIN_ID, HEAD, RPC]) Name() *mockNodeSelector_Name_Call[CHAIN_ID, HEAD, RPC] { - return &mockNodeSelector_Name_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("Name")} +func (_e *mockNodeSelector_Expecter[CHAIN_ID, RPC]) Name() *mockNodeSelector_Name_Call[CHAIN_ID, RPC] { + return &mockNodeSelector_Name_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Name")} } -func (_c *mockNodeSelector_Name_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNodeSelector_Name_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNodeSelector_Name_Call[CHAIN_ID, RPC]) Run(run func()) *mockNodeSelector_Name_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNodeSelector_Name_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 string) *mockNodeSelector_Name_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNodeSelector_Name_Call[CHAIN_ID, RPC]) Return(_a0 string) *mockNodeSelector_Name_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNodeSelector_Name_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() string) *mockNodeSelector_Name_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNodeSelector_Name_Call[CHAIN_ID, RPC]) RunAndReturn(run func() string) *mockNodeSelector_Name_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // Select provides a mock function with given fields: -func (_m *mockNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, RPC] { +func (_m *mockNodeSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] { ret := _m.Called() if len(ret) == 0 { panic("no return value specified for Select") } - var r0 Node[CHAIN_ID, HEAD, RPC] - if rf, ok := ret.Get(0).(func() Node[CHAIN_ID, HEAD, RPC]); ok { + var r0 Node[CHAIN_ID, RPC] + if rf, ok := ret.Get(0).(func() Node[CHAIN_ID, RPC]); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(Node[CHAIN_ID, HEAD, RPC]) + r0 = ret.Get(0).(Node[CHAIN_ID, RPC]) } } @@ -86,39 +86,39 @@ func (_m *mockNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, R } // mockNodeSelector_Select_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Select' -type mockNodeSelector_Select_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNodeSelector_Select_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // Select is a helper method to define mock.On call -func (_e *mockNodeSelector_Expecter[CHAIN_ID, HEAD, RPC]) Select() *mockNodeSelector_Select_Call[CHAIN_ID, HEAD, RPC] { - return &mockNodeSelector_Select_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("Select")} +func (_e *mockNodeSelector_Expecter[CHAIN_ID, RPC]) Select() *mockNodeSelector_Select_Call[CHAIN_ID, RPC] { + return &mockNodeSelector_Select_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Select")} } -func (_c *mockNodeSelector_Select_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNodeSelector_Select_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNodeSelector_Select_Call[CHAIN_ID, RPC]) Run(run func()) *mockNodeSelector_Select_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNodeSelector_Select_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 Node[CHAIN_ID, HEAD, RPC]) *mockNodeSelector_Select_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNodeSelector_Select_Call[CHAIN_ID, RPC]) Return(_a0 Node[CHAIN_ID, RPC]) *mockNodeSelector_Select_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNodeSelector_Select_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() Node[CHAIN_ID, HEAD, RPC]) *mockNodeSelector_Select_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNodeSelector_Select_Call[CHAIN_ID, RPC]) RunAndReturn(run func() Node[CHAIN_ID, RPC]) *mockNodeSelector_Select_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // newMockNodeSelector creates a new instance of mockNodeSelector. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. -func newMockNodeSelector[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]](t interface { +func newMockNodeSelector[CHAIN_ID types.ID, RPC interface{}](t interface { mock.TestingT Cleanup(func()) -}) *mockNodeSelector[CHAIN_ID, HEAD, RPC] { - mock := &mockNodeSelector[CHAIN_ID, HEAD, RPC]{} +}) *mockNodeSelector[CHAIN_ID, RPC] { + mock := &mockNodeSelector[CHAIN_ID, RPC]{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/common/client/mock_node_test.go b/common/client/mock_node_test.go index a342a533a7d..968f980b988 100644 --- a/common/client/mock_node_test.go +++ b/common/client/mock_node_test.go @@ -10,20 +10,20 @@ import ( ) // mockNode is an autogenerated mock type for the Node type -type mockNode[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode[CHAIN_ID types.ID, RPC interface{}] struct { mock.Mock } -type mockNode_Expecter[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_Expecter[CHAIN_ID types.ID, RPC interface{}] struct { mock *mock.Mock } -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) EXPECT() *mockNode_Expecter[CHAIN_ID, HEAD, RPC] { - return &mockNode_Expecter[CHAIN_ID, HEAD, RPC]{mock: &_m.Mock} +func (_m *mockNode[CHAIN_ID, RPC]) EXPECT() *mockNode_Expecter[CHAIN_ID, RPC] { + return &mockNode_Expecter[CHAIN_ID, RPC]{mock: &_m.Mock} } // Close provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Close() error { +func (_m *mockNode[CHAIN_ID, RPC]) Close() error { ret := _m.Called() if len(ret) == 0 { @@ -41,34 +41,34 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Close() error { } // mockNode_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type mockNode_Close_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_Close_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // Close is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) Close() *mockNode_Close_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_Close_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("Close")} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) Close() *mockNode_Close_Call[CHAIN_ID, RPC] { + return &mockNode_Close_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Close")} } -func (_c *mockNode_Close_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_Close_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Close_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_Close_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNode_Close_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 error) *mockNode_Close_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Close_Call[CHAIN_ID, RPC]) Return(_a0 error) *mockNode_Close_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNode_Close_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() error) *mockNode_Close_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Close_Call[CHAIN_ID, RPC]) RunAndReturn(run func() error) *mockNode_Close_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // ConfiguredChainID provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) ConfiguredChainID() CHAIN_ID { +func (_m *mockNode[CHAIN_ID, RPC]) ConfiguredChainID() CHAIN_ID { ret := _m.Called() if len(ret) == 0 { @@ -86,34 +86,34 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) ConfiguredChainID() CHAIN_ID { } // mockNode_ConfiguredChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConfiguredChainID' -type mockNode_ConfiguredChainID_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_ConfiguredChainID_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // ConfiguredChainID is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) ConfiguredChainID() *mockNode_ConfiguredChainID_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_ConfiguredChainID_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("ConfiguredChainID")} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) ConfiguredChainID() *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC] { + return &mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC]{Call: _e.mock.On("ConfiguredChainID")} } -func (_c *mockNode_ConfiguredChainID_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_ConfiguredChainID_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNode_ConfiguredChainID_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 CHAIN_ID) *mockNode_ConfiguredChainID_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC]) Return(_a0 CHAIN_ID) *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNode_ConfiguredChainID_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() CHAIN_ID) *mockNode_ConfiguredChainID_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC]) RunAndReturn(run func() CHAIN_ID) *mockNode_ConfiguredChainID_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // HighestUserObservations provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) HighestUserObservations() ChainInfo { +func (_m *mockNode[CHAIN_ID, RPC]) HighestUserObservations() ChainInfo { ret := _m.Called() if len(ret) == 0 { @@ -131,34 +131,34 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) HighestUserObservations() ChainInfo { } // mockNode_HighestUserObservations_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HighestUserObservations' -type mockNode_HighestUserObservations_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_HighestUserObservations_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // HighestUserObservations is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) HighestUserObservations() *mockNode_HighestUserObservations_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_HighestUserObservations_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("HighestUserObservations")} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) HighestUserObservations() *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC] { + return &mockNode_HighestUserObservations_Call[CHAIN_ID, RPC]{Call: _e.mock.On("HighestUserObservations")} } -func (_c *mockNode_HighestUserObservations_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_HighestUserObservations_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNode_HighestUserObservations_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 ChainInfo) *mockNode_HighestUserObservations_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC]) Return(_a0 ChainInfo) *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNode_HighestUserObservations_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() ChainInfo) *mockNode_HighestUserObservations_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC]) RunAndReturn(run func() ChainInfo) *mockNode_HighestUserObservations_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // Name provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Name() string { +func (_m *mockNode[CHAIN_ID, RPC]) Name() string { ret := _m.Called() if len(ret) == 0 { @@ -176,34 +176,34 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Name() string { } // mockNode_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' -type mockNode_Name_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_Name_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // Name is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) Name() *mockNode_Name_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_Name_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("Name")} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) Name() *mockNode_Name_Call[CHAIN_ID, RPC] { + return &mockNode_Name_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Name")} } -func (_c *mockNode_Name_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_Name_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Name_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_Name_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNode_Name_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 string) *mockNode_Name_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Name_Call[CHAIN_ID, RPC]) Return(_a0 string) *mockNode_Name_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNode_Name_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() string) *mockNode_Name_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Name_Call[CHAIN_ID, RPC]) RunAndReturn(run func() string) *mockNode_Name_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // Order provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Order() int32 { +func (_m *mockNode[CHAIN_ID, RPC]) Order() int32 { ret := _m.Called() if len(ret) == 0 { @@ -221,34 +221,34 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Order() int32 { } // mockNode_Order_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Order' -type mockNode_Order_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_Order_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // Order is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) Order() *mockNode_Order_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_Order_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("Order")} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) Order() *mockNode_Order_Call[CHAIN_ID, RPC] { + return &mockNode_Order_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Order")} } -func (_c *mockNode_Order_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_Order_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Order_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_Order_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNode_Order_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 int32) *mockNode_Order_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Order_Call[CHAIN_ID, RPC]) Return(_a0 int32) *mockNode_Order_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNode_Order_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() int32) *mockNode_Order_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Order_Call[CHAIN_ID, RPC]) RunAndReturn(run func() int32) *mockNode_Order_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // RPC provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) RPC() RPC { +func (_m *mockNode[CHAIN_ID, RPC]) RPC() RPC { ret := _m.Called() if len(ret) == 0 { @@ -266,67 +266,67 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) RPC() RPC { } // mockNode_RPC_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RPC' -type mockNode_RPC_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_RPC_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // RPC is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) RPC() *mockNode_RPC_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_RPC_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("RPC")} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) RPC() *mockNode_RPC_Call[CHAIN_ID, RPC] { + return &mockNode_RPC_Call[CHAIN_ID, RPC]{Call: _e.mock.On("RPC")} } -func (_c *mockNode_RPC_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_RPC_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_RPC_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_RPC_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNode_RPC_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 RPC) *mockNode_RPC_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_RPC_Call[CHAIN_ID, RPC]) Return(_a0 RPC) *mockNode_RPC_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNode_RPC_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() RPC) *mockNode_RPC_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_RPC_Call[CHAIN_ID, RPC]) RunAndReturn(run func() RPC) *mockNode_RPC_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // SetPoolChainInfoProvider provides a mock function with given fields: _a0 -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) SetPoolChainInfoProvider(_a0 PoolChainInfoProvider) { +func (_m *mockNode[CHAIN_ID, RPC]) SetPoolChainInfoProvider(_a0 PoolChainInfoProvider) { _m.Called(_a0) } // mockNode_SetPoolChainInfoProvider_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPoolChainInfoProvider' -type mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // SetPoolChainInfoProvider is a helper method to define mock.On call // - _a0 PoolChainInfoProvider -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) SetPoolChainInfoProvider(_a0 interface{}) *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("SetPoolChainInfoProvider", _a0)} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) SetPoolChainInfoProvider(_a0 interface{}) *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC] { + return &mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC]{Call: _e.mock.On("SetPoolChainInfoProvider", _a0)} } -func (_c *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, HEAD, RPC]) Run(run func(_a0 PoolChainInfoProvider)) *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC]) Run(run func(_a0 PoolChainInfoProvider)) *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run(args[0].(PoolChainInfoProvider)) }) return _c } -func (_c *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, HEAD, RPC]) Return() *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC]) Return() *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC] { _c.Call.Return() return _c } -func (_c *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func(PoolChainInfoProvider)) *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC]) RunAndReturn(run func(PoolChainInfoProvider)) *mockNode_SetPoolChainInfoProvider_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // Start provides a mock function with given fields: _a0 -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Start(_a0 context.Context) error { +func (_m *mockNode[CHAIN_ID, RPC]) Start(_a0 context.Context) error { ret := _m.Called(_a0) if len(ret) == 0 { @@ -344,35 +344,35 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Start(_a0 context.Context) error { } // mockNode_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start' -type mockNode_Start_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_Start_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // Start is a helper method to define mock.On call // - _a0 context.Context -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) Start(_a0 interface{}) *mockNode_Start_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_Start_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("Start", _a0)} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) Start(_a0 interface{}) *mockNode_Start_Call[CHAIN_ID, RPC] { + return &mockNode_Start_Call[CHAIN_ID, RPC]{Call: _e.mock.On("Start", _a0)} } -func (_c *mockNode_Start_Call[CHAIN_ID, HEAD, RPC]) Run(run func(_a0 context.Context)) *mockNode_Start_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Start_Call[CHAIN_ID, RPC]) Run(run func(_a0 context.Context)) *mockNode_Start_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context)) }) return _c } -func (_c *mockNode_Start_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 error) *mockNode_Start_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Start_Call[CHAIN_ID, RPC]) Return(_a0 error) *mockNode_Start_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNode_Start_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func(context.Context) error) *mockNode_Start_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_Start_Call[CHAIN_ID, RPC]) RunAndReturn(run func(context.Context) error) *mockNode_Start_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // State provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) State() nodeState { +func (_m *mockNode[CHAIN_ID, RPC]) State() nodeState { ret := _m.Called() if len(ret) == 0 { @@ -390,34 +390,34 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) State() nodeState { } // mockNode_State_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'State' -type mockNode_State_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_State_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // State is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) State() *mockNode_State_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_State_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("State")} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) State() *mockNode_State_Call[CHAIN_ID, RPC] { + return &mockNode_State_Call[CHAIN_ID, RPC]{Call: _e.mock.On("State")} } -func (_c *mockNode_State_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_State_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_State_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_State_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNode_State_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 nodeState) *mockNode_State_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_State_Call[CHAIN_ID, RPC]) Return(_a0 nodeState) *mockNode_State_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNode_State_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() nodeState) *mockNode_State_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_State_Call[CHAIN_ID, RPC]) RunAndReturn(run func() nodeState) *mockNode_State_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // StateAndLatest provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, ChainInfo) { +func (_m *mockNode[CHAIN_ID, RPC]) StateAndLatest() (nodeState, ChainInfo) { ret := _m.Called() if len(ret) == 0 { @@ -445,34 +445,34 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, ChainInfo) } // mockNode_StateAndLatest_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StateAndLatest' -type mockNode_StateAndLatest_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_StateAndLatest_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // StateAndLatest is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) StateAndLatest() *mockNode_StateAndLatest_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_StateAndLatest_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("StateAndLatest")} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) StateAndLatest() *mockNode_StateAndLatest_Call[CHAIN_ID, RPC] { + return &mockNode_StateAndLatest_Call[CHAIN_ID, RPC]{Call: _e.mock.On("StateAndLatest")} } -func (_c *mockNode_StateAndLatest_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_StateAndLatest_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_StateAndLatest_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_StateAndLatest_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNode_StateAndLatest_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 nodeState, _a1 ChainInfo) *mockNode_StateAndLatest_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_StateAndLatest_Call[CHAIN_ID, RPC]) Return(_a0 nodeState, _a1 ChainInfo) *mockNode_StateAndLatest_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0, _a1) return _c } -func (_c *mockNode_StateAndLatest_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() (nodeState, ChainInfo)) *mockNode_StateAndLatest_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_StateAndLatest_Call[CHAIN_ID, RPC]) RunAndReturn(run func() (nodeState, ChainInfo)) *mockNode_StateAndLatest_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // String provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) String() string { +func (_m *mockNode[CHAIN_ID, RPC]) String() string { ret := _m.Called() if len(ret) == 0 { @@ -490,116 +490,71 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) String() string { } // mockNode_String_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' -type mockNode_String_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_String_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // String is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) String() *mockNode_String_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_String_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("String")} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) String() *mockNode_String_Call[CHAIN_ID, RPC] { + return &mockNode_String_Call[CHAIN_ID, RPC]{Call: _e.mock.On("String")} } -func (_c *mockNode_String_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_String_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_String_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_String_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNode_String_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 string) *mockNode_String_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_String_Call[CHAIN_ID, RPC]) Return(_a0 string) *mockNode_String_Call[CHAIN_ID, RPC] { _c.Call.Return(_a0) return _c } -func (_c *mockNode_String_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() string) *mockNode_String_Call[CHAIN_ID, HEAD, RPC] { - _c.Call.Return(run) - return _c -} - -// SubscribersCount provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) SubscribersCount() int32 { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for SubscribersCount") - } - - var r0 int32 - if rf, ok := ret.Get(0).(func() int32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int32) - } - - return r0 -} - -// mockNode_SubscribersCount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribersCount' -type mockNode_SubscribersCount_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { - *mock.Call -} - -// SubscribersCount is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) SubscribersCount() *mockNode_SubscribersCount_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_SubscribersCount_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("SubscribersCount")} -} - -func (_c *mockNode_SubscribersCount_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_SubscribersCount_Call[CHAIN_ID, HEAD, RPC] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockNode_SubscribersCount_Call[CHAIN_ID, HEAD, RPC]) Return(_a0 int32) *mockNode_SubscribersCount_Call[CHAIN_ID, HEAD, RPC] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockNode_SubscribersCount_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func() int32) *mockNode_SubscribersCount_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_String_Call[CHAIN_ID, RPC]) RunAndReturn(run func() string) *mockNode_String_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // UnsubscribeAllExceptAliveLoop provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) UnsubscribeAllExceptAliveLoop() { +func (_m *mockNode[CHAIN_ID, RPC]) UnsubscribeAllExceptAliveLoop() { _m.Called() } // mockNode_UnsubscribeAllExceptAliveLoop_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnsubscribeAllExceptAliveLoop' -type mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { +type mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } // UnsubscribeAllExceptAliveLoop is a helper method to define mock.On call -func (_e *mockNode_Expecter[CHAIN_ID, HEAD, RPC]) UnsubscribeAllExceptAliveLoop() *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD, RPC] { - return &mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD, RPC]{Call: _e.mock.On("UnsubscribeAllExceptAliveLoop")} +func (_e *mockNode_Expecter[CHAIN_ID, RPC]) UnsubscribeAllExceptAliveLoop() *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC] { + return &mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC]{Call: _e.mock.On("UnsubscribeAllExceptAliveLoop")} } -func (_c *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD, RPC]) Run(run func()) *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC]) Run(run func()) *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC] { _c.Call.Run(func(args mock.Arguments) { run() }) return _c } -func (_c *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD, RPC]) Return() *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC]) Return() *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC] { _c.Call.Return() return _c } -func (_c *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD, RPC]) RunAndReturn(run func()) *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, HEAD, RPC] { +func (_c *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC]) RunAndReturn(run func()) *mockNode_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, RPC] { _c.Call.Return(run) return _c } // newMockNode creates a new instance of mockNode. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. -func newMockNode[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]](t interface { +func newMockNode[CHAIN_ID types.ID, RPC interface{}](t interface { mock.TestingT Cleanup(func()) -}) *mockNode[CHAIN_ID, HEAD, RPC] { - mock := &mockNode[CHAIN_ID, HEAD, RPC]{} +}) *mockNode[CHAIN_ID, RPC] { + mock := &mockNode[CHAIN_ID, RPC]{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/common/client/mock_rpc_client_test.go b/common/client/mock_rpc_client_test.go new file mode 100644 index 00000000000..30452ded77b --- /dev/null +++ b/common/client/mock_rpc_client_test.go @@ -0,0 +1,508 @@ +// Code generated by mockery v2.43.2. DO NOT EDIT. + +package client + +import ( + context "context" + + types "github.com/smartcontractkit/chainlink/v2/common/types" + mock "github.com/stretchr/testify/mock" +) + +// mockRPCClient is an autogenerated mock type for the RPCClient type +type mockRPCClient[CHAIN_ID types.ID, HEAD Head] struct { + mock.Mock +} + +type mockRPCClient_Expecter[CHAIN_ID types.ID, HEAD Head] struct { + mock *mock.Mock +} + +func (_m *mockRPCClient[CHAIN_ID, HEAD]) EXPECT() *mockRPCClient_Expecter[CHAIN_ID, HEAD] { + return &mockRPCClient_Expecter[CHAIN_ID, HEAD]{mock: &_m.Mock} +} + +// ChainID provides a mock function with given fields: ctx +func (_m *mockRPCClient[CHAIN_ID, HEAD]) ChainID(ctx context.Context) (CHAIN_ID, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for ChainID") + } + + var r0 CHAIN_ID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (CHAIN_ID, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) CHAIN_ID); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(CHAIN_ID) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockRPCClient_ChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChainID' +type mockRPCClient_ChainID_Call[CHAIN_ID types.ID, HEAD Head] struct { + *mock.Call +} + +// ChainID is a helper method to define mock.On call +// - ctx context.Context +func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) ChainID(ctx interface{}) *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD] { + return &mockRPCClient_ChainID_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("ChainID", ctx)} +} + +func (_c *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD] { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD]) Return(_a0 CHAIN_ID, _a1 error) *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD] { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (CHAIN_ID, error)) *mockRPCClient_ChainID_Call[CHAIN_ID, HEAD] { + _c.Call.Return(run) + return _c +} + +// Close provides a mock function with given fields: +func (_m *mockRPCClient[CHAIN_ID, HEAD]) Close() { + _m.Called() +} + +// mockRPCClient_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' +type mockRPCClient_Close_Call[CHAIN_ID types.ID, HEAD Head] struct { + *mock.Call +} + +// Close is a helper method to define mock.On call +func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) Close() *mockRPCClient_Close_Call[CHAIN_ID, HEAD] { + return &mockRPCClient_Close_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("Close")} +} + +func (_c *mockRPCClient_Close_Call[CHAIN_ID, HEAD]) Run(run func()) *mockRPCClient_Close_Call[CHAIN_ID, HEAD] { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockRPCClient_Close_Call[CHAIN_ID, HEAD]) Return() *mockRPCClient_Close_Call[CHAIN_ID, HEAD] { + _c.Call.Return() + return _c +} + +func (_c *mockRPCClient_Close_Call[CHAIN_ID, HEAD]) RunAndReturn(run func()) *mockRPCClient_Close_Call[CHAIN_ID, HEAD] { + _c.Call.Return(run) + return _c +} + +// Dial provides a mock function with given fields: ctx +func (_m *mockRPCClient[CHAIN_ID, HEAD]) Dial(ctx context.Context) error { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for Dial") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockRPCClient_Dial_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Dial' +type mockRPCClient_Dial_Call[CHAIN_ID types.ID, HEAD Head] struct { + *mock.Call +} + +// Dial is a helper method to define mock.On call +// - ctx context.Context +func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) Dial(ctx interface{}) *mockRPCClient_Dial_Call[CHAIN_ID, HEAD] { + return &mockRPCClient_Dial_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("Dial", ctx)} +} + +func (_c *mockRPCClient_Dial_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockRPCClient_Dial_Call[CHAIN_ID, HEAD] { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *mockRPCClient_Dial_Call[CHAIN_ID, HEAD]) Return(_a0 error) *mockRPCClient_Dial_Call[CHAIN_ID, HEAD] { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockRPCClient_Dial_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) error) *mockRPCClient_Dial_Call[CHAIN_ID, HEAD] { + _c.Call.Return(run) + return _c +} + +// GetInterceptedChainInfo provides a mock function with given fields: +func (_m *mockRPCClient[CHAIN_ID, HEAD]) GetInterceptedChainInfo() (ChainInfo, ChainInfo) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetInterceptedChainInfo") + } + + var r0 ChainInfo + var r1 ChainInfo + if rf, ok := ret.Get(0).(func() (ChainInfo, ChainInfo)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() ChainInfo); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(ChainInfo) + } + + if rf, ok := ret.Get(1).(func() ChainInfo); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(ChainInfo) + } + + return r0, r1 +} + +// mockRPCClient_GetInterceptedChainInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetInterceptedChainInfo' +type mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID types.ID, HEAD Head] struct { + *mock.Call +} + +// GetInterceptedChainInfo is a helper method to define mock.On call +func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) GetInterceptedChainInfo() *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] { + return &mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("GetInterceptedChainInfo")} +} + +func (_c *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]) Run(run func()) *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]) Return(latest ChainInfo, highestUserObservations ChainInfo) *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] { + _c.Call.Return(latest, highestUserObservations) + return _c +} + +func (_c *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD]) RunAndReturn(run func() (ChainInfo, ChainInfo)) *mockRPCClient_GetInterceptedChainInfo_Call[CHAIN_ID, HEAD] { + _c.Call.Return(run) + return _c +} + +// IsSyncing provides a mock function with given fields: ctx +func (_m *mockRPCClient[CHAIN_ID, HEAD]) IsSyncing(ctx context.Context) (bool, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for IsSyncing") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (bool, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) bool); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockRPCClient_IsSyncing_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsSyncing' +type mockRPCClient_IsSyncing_Call[CHAIN_ID types.ID, HEAD Head] struct { + *mock.Call +} + +// IsSyncing is a helper method to define mock.On call +// - ctx context.Context +func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) IsSyncing(ctx interface{}) *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD] { + return &mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("IsSyncing", ctx)} +} + +func (_c *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD] { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD]) Return(_a0 bool, _a1 error) *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD] { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (bool, error)) *mockRPCClient_IsSyncing_Call[CHAIN_ID, HEAD] { + _c.Call.Return(run) + return _c +} + +// Ping provides a mock function with given fields: _a0 +func (_m *mockRPCClient[CHAIN_ID, HEAD]) Ping(_a0 context.Context) error { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for Ping") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockRPCClient_Ping_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Ping' +type mockRPCClient_Ping_Call[CHAIN_ID types.ID, HEAD Head] struct { + *mock.Call +} + +// Ping is a helper method to define mock.On call +// - _a0 context.Context +func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) Ping(_a0 interface{}) *mockRPCClient_Ping_Call[CHAIN_ID, HEAD] { + return &mockRPCClient_Ping_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("Ping", _a0)} +} + +func (_c *mockRPCClient_Ping_Call[CHAIN_ID, HEAD]) Run(run func(_a0 context.Context)) *mockRPCClient_Ping_Call[CHAIN_ID, HEAD] { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *mockRPCClient_Ping_Call[CHAIN_ID, HEAD]) Return(_a0 error) *mockRPCClient_Ping_Call[CHAIN_ID, HEAD] { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockRPCClient_Ping_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) error) *mockRPCClient_Ping_Call[CHAIN_ID, HEAD] { + _c.Call.Return(run) + return _c +} + +// SubscribeToFinalizedHeads provides a mock function with given fields: ctx +func (_m *mockRPCClient[CHAIN_ID, HEAD]) SubscribeToFinalizedHeads(ctx context.Context) (<-chan HEAD, types.Subscription, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for SubscribeToFinalizedHeads") + } + + var r0 <-chan HEAD + var r1 types.Subscription + var r2 error + if rf, ok := ret.Get(0).(func(context.Context) (<-chan HEAD, types.Subscription, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) <-chan HEAD); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(<-chan HEAD) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) types.Subscription); ok { + r1 = rf(ctx) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(types.Subscription) + } + } + + if rf, ok := ret.Get(2).(func(context.Context) error); ok { + r2 = rf(ctx) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// mockRPCClient_SubscribeToFinalizedHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToFinalizedHeads' +type mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID types.ID, HEAD Head] struct { + *mock.Call +} + +// SubscribeToFinalizedHeads is a helper method to define mock.On call +// - ctx context.Context +func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) SubscribeToFinalizedHeads(ctx interface{}) *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] { + return &mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("SubscribeToFinalizedHeads", ctx)} +} + +func (_c *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]) Return(_a0 <-chan HEAD, _a1 types.Subscription, _a2 error) *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (<-chan HEAD, types.Subscription, error)) *mockRPCClient_SubscribeToFinalizedHeads_Call[CHAIN_ID, HEAD] { + _c.Call.Return(run) + return _c +} + +// SubscribeToHeads provides a mock function with given fields: ctx +func (_m *mockRPCClient[CHAIN_ID, HEAD]) SubscribeToHeads(ctx context.Context) (<-chan HEAD, types.Subscription, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for SubscribeToHeads") + } + + var r0 <-chan HEAD + var r1 types.Subscription + var r2 error + if rf, ok := ret.Get(0).(func(context.Context) (<-chan HEAD, types.Subscription, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) <-chan HEAD); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(<-chan HEAD) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) types.Subscription); ok { + r1 = rf(ctx) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(types.Subscription) + } + } + + if rf, ok := ret.Get(2).(func(context.Context) error); ok { + r2 = rf(ctx) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// mockRPCClient_SubscribeToHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToHeads' +type mockRPCClient_SubscribeToHeads_Call[CHAIN_ID types.ID, HEAD Head] struct { + *mock.Call +} + +// SubscribeToHeads is a helper method to define mock.On call +// - ctx context.Context +func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) SubscribeToHeads(ctx interface{}) *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] { + return &mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("SubscribeToHeads", ctx)} +} + +func (_c *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]) Run(run func(ctx context.Context)) *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]) Return(_a0 <-chan HEAD, _a1 types.Subscription, _a2 error) *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(context.Context) (<-chan HEAD, types.Subscription, error)) *mockRPCClient_SubscribeToHeads_Call[CHAIN_ID, HEAD] { + _c.Call.Return(run) + return _c +} + +// UnsubscribeAllExcept provides a mock function with given fields: subs +func (_m *mockRPCClient[CHAIN_ID, HEAD]) UnsubscribeAllExcept(subs ...types.Subscription) { + _va := make([]interface{}, len(subs)) + for _i := range subs { + _va[_i] = subs[_i] + } + var _ca []interface{} + _ca = append(_ca, _va...) + _m.Called(_ca...) +} + +// mockRPCClient_UnsubscribeAllExcept_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnsubscribeAllExcept' +type mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID types.ID, HEAD Head] struct { + *mock.Call +} + +// UnsubscribeAllExcept is a helper method to define mock.On call +// - subs ...types.Subscription +func (_e *mockRPCClient_Expecter[CHAIN_ID, HEAD]) UnsubscribeAllExcept(subs ...interface{}) *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD] { + return &mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD]{Call: _e.mock.On("UnsubscribeAllExcept", + append([]interface{}{}, subs...)...)} +} + +func (_c *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD]) Run(run func(subs ...types.Subscription)) *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD] { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]types.Subscription, len(args)-0) + for i, a := range args[0:] { + if a != nil { + variadicArgs[i] = a.(types.Subscription) + } + } + run(variadicArgs...) + }) + return _c +} + +func (_c *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD]) Return() *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD] { + _c.Call.Return() + return _c +} + +func (_c *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD]) RunAndReturn(run func(...types.Subscription)) *mockRPCClient_UnsubscribeAllExcept_Call[CHAIN_ID, HEAD] { + _c.Call.Return(run) + return _c +} + +// newMockRPCClient creates a new instance of mockRPCClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockRPCClient[CHAIN_ID types.ID, HEAD Head](t interface { + mock.TestingT + Cleanup(func()) +}) *mockRPCClient[CHAIN_ID, HEAD] { + mock := &mockRPCClient[CHAIN_ID, HEAD]{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/common/client/mock_rpc_test.go b/common/client/mock_rpc_test.go deleted file mode 100644 index 00473c66369..00000000000 --- a/common/client/mock_rpc_test.go +++ /dev/null @@ -1,1918 +0,0 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. - -package client - -import ( - big "math/big" - - assets "github.com/smartcontractkit/chainlink-common/pkg/assets" - - context "context" - - feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" - - mock "github.com/stretchr/testify/mock" - - types "github.com/smartcontractkit/chainlink/v2/common/types" -) - -// mockRPC is an autogenerated mock type for the RPC type -type mockRPC[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - mock.Mock -} - -type mockRPC_Expecter[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - mock *mock.Mock -} - -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) EXPECT() *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{mock: &_m.Mock} -} - -// BalanceAt provides a mock function with given fields: ctx, accountAddress, blockNumber -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BalanceAt(ctx context.Context, accountAddress ADDR, blockNumber *big.Int) (*big.Int, error) { - ret := _m.Called(ctx, accountAddress, blockNumber) - - if len(ret) == 0 { - panic("no return value specified for BalanceAt") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, ADDR, *big.Int) (*big.Int, error)); ok { - return rf(ctx, accountAddress, blockNumber) - } - if rf, ok := ret.Get(0).(func(context.Context, ADDR, *big.Int) *big.Int); ok { - r0 = rf(ctx, accountAddress, blockNumber) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, ADDR, *big.Int) error); ok { - r1 = rf(ctx, accountAddress, blockNumber) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_BalanceAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BalanceAt' -type mockRPC_BalanceAt_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// BalanceAt is a helper method to define mock.On call -// - ctx context.Context -// - accountAddress ADDR -// - blockNumber *big.Int -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BalanceAt(ctx interface{}, accountAddress interface{}, blockNumber interface{}) *mockRPC_BalanceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_BalanceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("BalanceAt", ctx, accountAddress, blockNumber)} -} - -func (_c *mockRPC_BalanceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, accountAddress ADDR, blockNumber *big.Int)) *mockRPC_BalanceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(ADDR), args[2].(*big.Int)) - }) - return _c -} - -func (_c *mockRPC_BalanceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 *big.Int, _a1 error) *mockRPC_BalanceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_BalanceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, ADDR, *big.Int) (*big.Int, error)) *mockRPC_BalanceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// BatchCallContext provides a mock function with given fields: ctx, b -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BatchCallContext(ctx context.Context, b []BATCH_ELEM) error { - ret := _m.Called(ctx, b) - - if len(ret) == 0 { - panic("no return value specified for BatchCallContext") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, []BATCH_ELEM) error); ok { - r0 = rf(ctx, b) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockRPC_BatchCallContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BatchCallContext' -type mockRPC_BatchCallContext_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// BatchCallContext is a helper method to define mock.On call -// - ctx context.Context -// - b []BATCH_ELEM -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BatchCallContext(ctx interface{}, b interface{}) *mockRPC_BatchCallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_BatchCallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("BatchCallContext", ctx, b)} -} - -func (_c *mockRPC_BatchCallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, b []BATCH_ELEM)) *mockRPC_BatchCallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]BATCH_ELEM)) - }) - return _c -} - -func (_c *mockRPC_BatchCallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 error) *mockRPC_BatchCallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockRPC_BatchCallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, []BATCH_ELEM) error) *mockRPC_BatchCallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// BlockByHash provides a mock function with given fields: ctx, hash -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BlockByHash(ctx context.Context, hash BLOCK_HASH) (HEAD, error) { - ret := _m.Called(ctx, hash) - - if len(ret) == 0 { - panic("no return value specified for BlockByHash") - } - - var r0 HEAD - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, BLOCK_HASH) (HEAD, error)); ok { - return rf(ctx, hash) - } - if rf, ok := ret.Get(0).(func(context.Context, BLOCK_HASH) HEAD); ok { - r0 = rf(ctx, hash) - } else { - r0 = ret.Get(0).(HEAD) - } - - if rf, ok := ret.Get(1).(func(context.Context, BLOCK_HASH) error); ok { - r1 = rf(ctx, hash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_BlockByHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockByHash' -type mockRPC_BlockByHash_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// BlockByHash is a helper method to define mock.On call -// - ctx context.Context -// - hash BLOCK_HASH -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BlockByHash(ctx interface{}, hash interface{}) *mockRPC_BlockByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_BlockByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("BlockByHash", ctx, hash)} -} - -func (_c *mockRPC_BlockByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, hash BLOCK_HASH)) *mockRPC_BlockByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(BLOCK_HASH)) - }) - return _c -} - -func (_c *mockRPC_BlockByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 HEAD, _a1 error) *mockRPC_BlockByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_BlockByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, BLOCK_HASH) (HEAD, error)) *mockRPC_BlockByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// BlockByNumber provides a mock function with given fields: ctx, number -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BlockByNumber(ctx context.Context, number *big.Int) (HEAD, error) { - ret := _m.Called(ctx, number) - - if len(ret) == 0 { - panic("no return value specified for BlockByNumber") - } - - var r0 HEAD - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (HEAD, error)); ok { - return rf(ctx, number) - } - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) HEAD); ok { - r0 = rf(ctx, number) - } else { - r0 = ret.Get(0).(HEAD) - } - - if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { - r1 = rf(ctx, number) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_BlockByNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockByNumber' -type mockRPC_BlockByNumber_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// BlockByNumber is a helper method to define mock.On call -// - ctx context.Context -// - number *big.Int -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BlockByNumber(ctx interface{}, number interface{}) *mockRPC_BlockByNumber_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_BlockByNumber_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("BlockByNumber", ctx, number)} -} - -func (_c *mockRPC_BlockByNumber_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, number *big.Int)) *mockRPC_BlockByNumber_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*big.Int)) - }) - return _c -} - -func (_c *mockRPC_BlockByNumber_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 HEAD, _a1 error) *mockRPC_BlockByNumber_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_BlockByNumber_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, *big.Int) (HEAD, error)) *mockRPC_BlockByNumber_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// CallContext provides a mock function with given fields: ctx, result, method, args -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { - var _ca []interface{} - _ca = append(_ca, ctx, result, method) - _ca = append(_ca, args...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for CallContext") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, interface{}, string, ...interface{}) error); ok { - r0 = rf(ctx, result, method, args...) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockRPC_CallContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CallContext' -type mockRPC_CallContext_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// CallContext is a helper method to define mock.On call -// - ctx context.Context -// - result interface{} -// - method string -// - args ...interface{} -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) CallContext(ctx interface{}, result interface{}, method interface{}, args ...interface{}) *mockRPC_CallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_CallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("CallContext", - append([]interface{}{ctx, result, method}, args...)...)} -} - -func (_c *mockRPC_CallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, result interface{}, method string, args ...interface{})) *mockRPC_CallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-3) - for i, a := range args[3:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(args[0].(context.Context), args[1].(interface{}), args[2].(string), variadicArgs...) - }) - return _c -} - -func (_c *mockRPC_CallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 error) *mockRPC_CallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockRPC_CallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, interface{}, string, ...interface{}) error) *mockRPC_CallContext_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// CallContract provides a mock function with given fields: ctx, msg, blockNumber -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) CallContract(ctx context.Context, msg interface{}, blockNumber *big.Int) ([]byte, error) { - ret := _m.Called(ctx, msg, blockNumber) - - if len(ret) == 0 { - panic("no return value specified for CallContract") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, interface{}, *big.Int) ([]byte, error)); ok { - return rf(ctx, msg, blockNumber) - } - if rf, ok := ret.Get(0).(func(context.Context, interface{}, *big.Int) []byte); ok { - r0 = rf(ctx, msg, blockNumber) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, interface{}, *big.Int) error); ok { - r1 = rf(ctx, msg, blockNumber) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_CallContract_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CallContract' -type mockRPC_CallContract_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// CallContract is a helper method to define mock.On call -// - ctx context.Context -// - msg interface{} -// - blockNumber *big.Int -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) CallContract(ctx interface{}, msg interface{}, blockNumber interface{}) *mockRPC_CallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_CallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("CallContract", ctx, msg, blockNumber)} -} - -func (_c *mockRPC_CallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, msg interface{}, blockNumber *big.Int)) *mockRPC_CallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(interface{}), args[2].(*big.Int)) - }) - return _c -} - -func (_c *mockRPC_CallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(rpcErr []byte, extractErr error) *mockRPC_CallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(rpcErr, extractErr) - return _c -} - -func (_c *mockRPC_CallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, interface{}, *big.Int) ([]byte, error)) *mockRPC_CallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// ChainID provides a mock function with given fields: ctx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) ChainID(ctx context.Context) (CHAIN_ID, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for ChainID") - } - - var r0 CHAIN_ID - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (CHAIN_ID, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) CHAIN_ID); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(CHAIN_ID) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_ChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChainID' -type mockRPC_ChainID_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// ChainID is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) ChainID(ctx interface{}) *mockRPC_ChainID_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_ChainID_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("ChainID", ctx)} -} - -func (_c *mockRPC_ChainID_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context)) *mockRPC_ChainID_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPC_ChainID_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 CHAIN_ID, _a1 error) *mockRPC_ChainID_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_ChainID_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context) (CHAIN_ID, error)) *mockRPC_ChainID_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// ClientVersion provides a mock function with given fields: _a0 -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) ClientVersion(_a0 context.Context) (string, error) { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for ClientVersion") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (string, error)); ok { - return rf(_a0) - } - if rf, ok := ret.Get(0).(func(context.Context) string); ok { - r0 = rf(_a0) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(_a0) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_ClientVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClientVersion' -type mockRPC_ClientVersion_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// ClientVersion is a helper method to define mock.On call -// - _a0 context.Context -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) ClientVersion(_a0 interface{}) *mockRPC_ClientVersion_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_ClientVersion_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("ClientVersion", _a0)} -} - -func (_c *mockRPC_ClientVersion_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(_a0 context.Context)) *mockRPC_ClientVersion_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPC_ClientVersion_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 string, _a1 error) *mockRPC_ClientVersion_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_ClientVersion_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context) (string, error)) *mockRPC_ClientVersion_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// Close provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Close() { - _m.Called() -} - -// mockRPC_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type mockRPC_Close_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// Close is a helper method to define mock.On call -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Close() *mockRPC_Close_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_Close_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("Close")} -} - -func (_c *mockRPC_Close_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func()) *mockRPC_Close_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockRPC_Close_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return() *mockRPC_Close_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return() - return _c -} - -func (_c *mockRPC_Close_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func()) *mockRPC_Close_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// CodeAt provides a mock function with given fields: ctx, account, blockNumber -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) CodeAt(ctx context.Context, account ADDR, blockNumber *big.Int) ([]byte, error) { - ret := _m.Called(ctx, account, blockNumber) - - if len(ret) == 0 { - panic("no return value specified for CodeAt") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, ADDR, *big.Int) ([]byte, error)); ok { - return rf(ctx, account, blockNumber) - } - if rf, ok := ret.Get(0).(func(context.Context, ADDR, *big.Int) []byte); ok { - r0 = rf(ctx, account, blockNumber) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, ADDR, *big.Int) error); ok { - r1 = rf(ctx, account, blockNumber) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_CodeAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CodeAt' -type mockRPC_CodeAt_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// CodeAt is a helper method to define mock.On call -// - ctx context.Context -// - account ADDR -// - blockNumber *big.Int -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) CodeAt(ctx interface{}, account interface{}, blockNumber interface{}) *mockRPC_CodeAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_CodeAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("CodeAt", ctx, account, blockNumber)} -} - -func (_c *mockRPC_CodeAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, account ADDR, blockNumber *big.Int)) *mockRPC_CodeAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(ADDR), args[2].(*big.Int)) - }) - return _c -} - -func (_c *mockRPC_CodeAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 []byte, _a1 error) *mockRPC_CodeAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_CodeAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, ADDR, *big.Int) ([]byte, error)) *mockRPC_CodeAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// Dial provides a mock function with given fields: ctx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Dial(ctx context.Context) error { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for Dial") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = rf(ctx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockRPC_Dial_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Dial' -type mockRPC_Dial_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// Dial is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Dial(ctx interface{}) *mockRPC_Dial_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_Dial_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("Dial", ctx)} -} - -func (_c *mockRPC_Dial_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context)) *mockRPC_Dial_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPC_Dial_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 error) *mockRPC_Dial_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockRPC_Dial_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context) error) *mockRPC_Dial_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// DialHTTP provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) DialHTTP() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for DialHTTP") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockRPC_DialHTTP_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DialHTTP' -type mockRPC_DialHTTP_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// DialHTTP is a helper method to define mock.On call -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) DialHTTP() *mockRPC_DialHTTP_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_DialHTTP_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("DialHTTP")} -} - -func (_c *mockRPC_DialHTTP_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func()) *mockRPC_DialHTTP_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockRPC_DialHTTP_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 error) *mockRPC_DialHTTP_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockRPC_DialHTTP_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func() error) *mockRPC_DialHTTP_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// DisconnectAll provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) DisconnectAll() { - _m.Called() -} - -// mockRPC_DisconnectAll_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectAll' -type mockRPC_DisconnectAll_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// DisconnectAll is a helper method to define mock.On call -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) DisconnectAll() *mockRPC_DisconnectAll_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_DisconnectAll_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("DisconnectAll")} -} - -func (_c *mockRPC_DisconnectAll_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func()) *mockRPC_DisconnectAll_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockRPC_DisconnectAll_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return() *mockRPC_DisconnectAll_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return() - return _c -} - -func (_c *mockRPC_DisconnectAll_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func()) *mockRPC_DisconnectAll_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// EstimateGas provides a mock function with given fields: ctx, call -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) EstimateGas(ctx context.Context, call interface{}) (uint64, error) { - ret := _m.Called(ctx, call) - - if len(ret) == 0 { - panic("no return value specified for EstimateGas") - } - - var r0 uint64 - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, interface{}) (uint64, error)); ok { - return rf(ctx, call) - } - if rf, ok := ret.Get(0).(func(context.Context, interface{}) uint64); ok { - r0 = rf(ctx, call) - } else { - r0 = ret.Get(0).(uint64) - } - - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, call) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_EstimateGas_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EstimateGas' -type mockRPC_EstimateGas_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// EstimateGas is a helper method to define mock.On call -// - ctx context.Context -// - call interface{} -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) EstimateGas(ctx interface{}, call interface{}) *mockRPC_EstimateGas_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_EstimateGas_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("EstimateGas", ctx, call)} -} - -func (_c *mockRPC_EstimateGas_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, call interface{})) *mockRPC_EstimateGas_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(interface{})) - }) - return _c -} - -func (_c *mockRPC_EstimateGas_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(gas uint64, err error) *mockRPC_EstimateGas_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(gas, err) - return _c -} - -func (_c *mockRPC_EstimateGas_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, interface{}) (uint64, error)) *mockRPC_EstimateGas_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// FilterEvents provides a mock function with given fields: ctx, query -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) FilterEvents(ctx context.Context, query EVENT_OPS) ([]EVENT, error) { - ret := _m.Called(ctx, query) - - if len(ret) == 0 { - panic("no return value specified for FilterEvents") - } - - var r0 []EVENT - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, EVENT_OPS) ([]EVENT, error)); ok { - return rf(ctx, query) - } - if rf, ok := ret.Get(0).(func(context.Context, EVENT_OPS) []EVENT); ok { - r0 = rf(ctx, query) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]EVENT) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, EVENT_OPS) error); ok { - r1 = rf(ctx, query) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_FilterEvents_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FilterEvents' -type mockRPC_FilterEvents_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// FilterEvents is a helper method to define mock.On call -// - ctx context.Context -// - query EVENT_OPS -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) FilterEvents(ctx interface{}, query interface{}) *mockRPC_FilterEvents_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_FilterEvents_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("FilterEvents", ctx, query)} -} - -func (_c *mockRPC_FilterEvents_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, query EVENT_OPS)) *mockRPC_FilterEvents_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(EVENT_OPS)) - }) - return _c -} - -func (_c *mockRPC_FilterEvents_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 []EVENT, _a1 error) *mockRPC_FilterEvents_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_FilterEvents_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, EVENT_OPS) ([]EVENT, error)) *mockRPC_FilterEvents_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// GetInterceptedChainInfo provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) GetInterceptedChainInfo() (ChainInfo, ChainInfo) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetInterceptedChainInfo") - } - - var r0 ChainInfo - var r1 ChainInfo - if rf, ok := ret.Get(0).(func() (ChainInfo, ChainInfo)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() ChainInfo); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(ChainInfo) - } - - if rf, ok := ret.Get(1).(func() ChainInfo); ok { - r1 = rf() - } else { - r1 = ret.Get(1).(ChainInfo) - } - - return r0, r1 -} - -// mockRPC_GetInterceptedChainInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetInterceptedChainInfo' -type mockRPC_GetInterceptedChainInfo_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// GetInterceptedChainInfo is a helper method to define mock.On call -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) GetInterceptedChainInfo() *mockRPC_GetInterceptedChainInfo_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_GetInterceptedChainInfo_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("GetInterceptedChainInfo")} -} - -func (_c *mockRPC_GetInterceptedChainInfo_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func()) *mockRPC_GetInterceptedChainInfo_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockRPC_GetInterceptedChainInfo_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(latest ChainInfo, highestUserObservations ChainInfo) *mockRPC_GetInterceptedChainInfo_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(latest, highestUserObservations) - return _c -} - -func (_c *mockRPC_GetInterceptedChainInfo_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func() (ChainInfo, ChainInfo)) *mockRPC_GetInterceptedChainInfo_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// IsSyncing provides a mock function with given fields: ctx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) IsSyncing(ctx context.Context) (bool, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for IsSyncing") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (bool, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) bool); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_IsSyncing_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsSyncing' -type mockRPC_IsSyncing_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// IsSyncing is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) IsSyncing(ctx interface{}) *mockRPC_IsSyncing_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_IsSyncing_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("IsSyncing", ctx)} -} - -func (_c *mockRPC_IsSyncing_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context)) *mockRPC_IsSyncing_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPC_IsSyncing_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 bool, _a1 error) *mockRPC_IsSyncing_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_IsSyncing_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context) (bool, error)) *mockRPC_IsSyncing_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// LINKBalance provides a mock function with given fields: ctx, accountAddress, linkAddress -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) LINKBalance(ctx context.Context, accountAddress ADDR, linkAddress ADDR) (*assets.Link, error) { - ret := _m.Called(ctx, accountAddress, linkAddress) - - if len(ret) == 0 { - panic("no return value specified for LINKBalance") - } - - var r0 *assets.Link - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, ADDR, ADDR) (*assets.Link, error)); ok { - return rf(ctx, accountAddress, linkAddress) - } - if rf, ok := ret.Get(0).(func(context.Context, ADDR, ADDR) *assets.Link); ok { - r0 = rf(ctx, accountAddress, linkAddress) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*assets.Link) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, ADDR, ADDR) error); ok { - r1 = rf(ctx, accountAddress, linkAddress) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_LINKBalance_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LINKBalance' -type mockRPC_LINKBalance_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// LINKBalance is a helper method to define mock.On call -// - ctx context.Context -// - accountAddress ADDR -// - linkAddress ADDR -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) LINKBalance(ctx interface{}, accountAddress interface{}, linkAddress interface{}) *mockRPC_LINKBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_LINKBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("LINKBalance", ctx, accountAddress, linkAddress)} -} - -func (_c *mockRPC_LINKBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, accountAddress ADDR, linkAddress ADDR)) *mockRPC_LINKBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(ADDR), args[2].(ADDR)) - }) - return _c -} - -func (_c *mockRPC_LINKBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 *assets.Link, _a1 error) *mockRPC_LINKBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_LINKBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, ADDR, ADDR) (*assets.Link, error)) *mockRPC_LINKBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// LatestBlockHeight provides a mock function with given fields: _a0 -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) LatestBlockHeight(_a0 context.Context) (*big.Int, error) { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for LatestBlockHeight") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { - return rf(_a0) - } - if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { - r0 = rf(_a0) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(_a0) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_LatestBlockHeight_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LatestBlockHeight' -type mockRPC_LatestBlockHeight_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// LatestBlockHeight is a helper method to define mock.On call -// - _a0 context.Context -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) LatestBlockHeight(_a0 interface{}) *mockRPC_LatestBlockHeight_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_LatestBlockHeight_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("LatestBlockHeight", _a0)} -} - -func (_c *mockRPC_LatestBlockHeight_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(_a0 context.Context)) *mockRPC_LatestBlockHeight_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPC_LatestBlockHeight_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 *big.Int, _a1 error) *mockRPC_LatestBlockHeight_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_LatestBlockHeight_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context) (*big.Int, error)) *mockRPC_LatestBlockHeight_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// LatestFinalizedBlock provides a mock function with given fields: ctx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) LatestFinalizedBlock(ctx context.Context) (HEAD, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for LatestFinalizedBlock") - } - - var r0 HEAD - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (HEAD, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) HEAD); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(HEAD) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_LatestFinalizedBlock_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LatestFinalizedBlock' -type mockRPC_LatestFinalizedBlock_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// LatestFinalizedBlock is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) LatestFinalizedBlock(ctx interface{}) *mockRPC_LatestFinalizedBlock_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_LatestFinalizedBlock_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("LatestFinalizedBlock", ctx)} -} - -func (_c *mockRPC_LatestFinalizedBlock_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context)) *mockRPC_LatestFinalizedBlock_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPC_LatestFinalizedBlock_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 HEAD, _a1 error) *mockRPC_LatestFinalizedBlock_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_LatestFinalizedBlock_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context) (HEAD, error)) *mockRPC_LatestFinalizedBlock_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// PendingCallContract provides a mock function with given fields: ctx, msg -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) PendingCallContract(ctx context.Context, msg interface{}) ([]byte, error) { - ret := _m.Called(ctx, msg) - - if len(ret) == 0 { - panic("no return value specified for PendingCallContract") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, interface{}) ([]byte, error)); ok { - return rf(ctx, msg) - } - if rf, ok := ret.Get(0).(func(context.Context, interface{}) []byte); ok { - r0 = rf(ctx, msg) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, msg) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_PendingCallContract_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PendingCallContract' -type mockRPC_PendingCallContract_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// PendingCallContract is a helper method to define mock.On call -// - ctx context.Context -// - msg interface{} -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) PendingCallContract(ctx interface{}, msg interface{}) *mockRPC_PendingCallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_PendingCallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("PendingCallContract", ctx, msg)} -} - -func (_c *mockRPC_PendingCallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, msg interface{})) *mockRPC_PendingCallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(interface{})) - }) - return _c -} - -func (_c *mockRPC_PendingCallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(rpcErr []byte, extractErr error) *mockRPC_PendingCallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(rpcErr, extractErr) - return _c -} - -func (_c *mockRPC_PendingCallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, interface{}) ([]byte, error)) *mockRPC_PendingCallContract_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// PendingSequenceAt provides a mock function with given fields: ctx, addr -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) PendingSequenceAt(ctx context.Context, addr ADDR) (SEQ, error) { - ret := _m.Called(ctx, addr) - - if len(ret) == 0 { - panic("no return value specified for PendingSequenceAt") - } - - var r0 SEQ - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, ADDR) (SEQ, error)); ok { - return rf(ctx, addr) - } - if rf, ok := ret.Get(0).(func(context.Context, ADDR) SEQ); ok { - r0 = rf(ctx, addr) - } else { - r0 = ret.Get(0).(SEQ) - } - - if rf, ok := ret.Get(1).(func(context.Context, ADDR) error); ok { - r1 = rf(ctx, addr) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_PendingSequenceAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PendingSequenceAt' -type mockRPC_PendingSequenceAt_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// PendingSequenceAt is a helper method to define mock.On call -// - ctx context.Context -// - addr ADDR -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) PendingSequenceAt(ctx interface{}, addr interface{}) *mockRPC_PendingSequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_PendingSequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("PendingSequenceAt", ctx, addr)} -} - -func (_c *mockRPC_PendingSequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, addr ADDR)) *mockRPC_PendingSequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(ADDR)) - }) - return _c -} - -func (_c *mockRPC_PendingSequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 SEQ, _a1 error) *mockRPC_PendingSequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_PendingSequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, ADDR) (SEQ, error)) *mockRPC_PendingSequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// SendEmptyTransaction provides a mock function with given fields: ctx, newTxAttempt, seq, gasLimit, fee, fromAddress -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SendEmptyTransaction(ctx context.Context, newTxAttempt func(SEQ, uint32, FEE, ADDR) (interface{}, error), seq SEQ, gasLimit uint32, fee FEE, fromAddress ADDR) (string, error) { - ret := _m.Called(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) - - if len(ret) == 0 { - panic("no return value specified for SendEmptyTransaction") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, func(SEQ, uint32, FEE, ADDR) (interface{}, error), SEQ, uint32, FEE, ADDR) (string, error)); ok { - return rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) - } - if rf, ok := ret.Get(0).(func(context.Context, func(SEQ, uint32, FEE, ADDR) (interface{}, error), SEQ, uint32, FEE, ADDR) string); ok { - r0 = rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(context.Context, func(SEQ, uint32, FEE, ADDR) (interface{}, error), SEQ, uint32, FEE, ADDR) error); ok { - r1 = rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_SendEmptyTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendEmptyTransaction' -type mockRPC_SendEmptyTransaction_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// SendEmptyTransaction is a helper method to define mock.On call -// - ctx context.Context -// - newTxAttempt func(SEQ , uint32 , FEE , ADDR)(interface{} , error) -// - seq SEQ -// - gasLimit uint32 -// - fee FEE -// - fromAddress ADDR -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SendEmptyTransaction(ctx interface{}, newTxAttempt interface{}, seq interface{}, gasLimit interface{}, fee interface{}, fromAddress interface{}) *mockRPC_SendEmptyTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_SendEmptyTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("SendEmptyTransaction", ctx, newTxAttempt, seq, gasLimit, fee, fromAddress)} -} - -func (_c *mockRPC_SendEmptyTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, newTxAttempt func(SEQ, uint32, FEE, ADDR) (interface{}, error), seq SEQ, gasLimit uint32, fee FEE, fromAddress ADDR)) *mockRPC_SendEmptyTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(func(SEQ, uint32, FEE, ADDR) (interface{}, error)), args[2].(SEQ), args[3].(uint32), args[4].(FEE), args[5].(ADDR)) - }) - return _c -} - -func (_c *mockRPC_SendEmptyTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(txhash string, err error) *mockRPC_SendEmptyTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(txhash, err) - return _c -} - -func (_c *mockRPC_SendEmptyTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, func(SEQ, uint32, FEE, ADDR) (interface{}, error), SEQ, uint32, FEE, ADDR) (string, error)) *mockRPC_SendEmptyTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// SendTransaction provides a mock function with given fields: ctx, tx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SendTransaction(ctx context.Context, tx TX) error { - ret := _m.Called(ctx, tx) - - if len(ret) == 0 { - panic("no return value specified for SendTransaction") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, TX) error); ok { - r0 = rf(ctx, tx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockRPC_SendTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendTransaction' -type mockRPC_SendTransaction_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// SendTransaction is a helper method to define mock.On call -// - ctx context.Context -// - tx TX -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SendTransaction(ctx interface{}, tx interface{}) *mockRPC_SendTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_SendTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("SendTransaction", ctx, tx)} -} - -func (_c *mockRPC_SendTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, tx TX)) *mockRPC_SendTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(TX)) - }) - return _c -} - -func (_c *mockRPC_SendTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 error) *mockRPC_SendTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockRPC_SendTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, TX) error) *mockRPC_SendTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// SequenceAt provides a mock function with given fields: ctx, accountAddress, blockNumber -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SequenceAt(ctx context.Context, accountAddress ADDR, blockNumber *big.Int) (SEQ, error) { - ret := _m.Called(ctx, accountAddress, blockNumber) - - if len(ret) == 0 { - panic("no return value specified for SequenceAt") - } - - var r0 SEQ - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, ADDR, *big.Int) (SEQ, error)); ok { - return rf(ctx, accountAddress, blockNumber) - } - if rf, ok := ret.Get(0).(func(context.Context, ADDR, *big.Int) SEQ); ok { - r0 = rf(ctx, accountAddress, blockNumber) - } else { - r0 = ret.Get(0).(SEQ) - } - - if rf, ok := ret.Get(1).(func(context.Context, ADDR, *big.Int) error); ok { - r1 = rf(ctx, accountAddress, blockNumber) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_SequenceAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SequenceAt' -type mockRPC_SequenceAt_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// SequenceAt is a helper method to define mock.On call -// - ctx context.Context -// - accountAddress ADDR -// - blockNumber *big.Int -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SequenceAt(ctx interface{}, accountAddress interface{}, blockNumber interface{}) *mockRPC_SequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_SequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("SequenceAt", ctx, accountAddress, blockNumber)} -} - -func (_c *mockRPC_SequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, accountAddress ADDR, blockNumber *big.Int)) *mockRPC_SequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(ADDR), args[2].(*big.Int)) - }) - return _c -} - -func (_c *mockRPC_SequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 SEQ, _a1 error) *mockRPC_SequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_SequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, ADDR, *big.Int) (SEQ, error)) *mockRPC_SequenceAt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// SetAliveLoopSub provides a mock function with given fields: _a0 -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SetAliveLoopSub(_a0 types.Subscription) { - _m.Called(_a0) -} - -// mockRPC_SetAliveLoopSub_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetAliveLoopSub' -type mockRPC_SetAliveLoopSub_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// SetAliveLoopSub is a helper method to define mock.On call -// - _a0 types.Subscription -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SetAliveLoopSub(_a0 interface{}) *mockRPC_SetAliveLoopSub_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_SetAliveLoopSub_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("SetAliveLoopSub", _a0)} -} - -func (_c *mockRPC_SetAliveLoopSub_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(_a0 types.Subscription)) *mockRPC_SetAliveLoopSub_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(types.Subscription)) - }) - return _c -} - -func (_c *mockRPC_SetAliveLoopSub_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return() *mockRPC_SetAliveLoopSub_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return() - return _c -} - -func (_c *mockRPC_SetAliveLoopSub_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(types.Subscription)) *mockRPC_SetAliveLoopSub_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// SimulateTransaction provides a mock function with given fields: ctx, tx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SimulateTransaction(ctx context.Context, tx TX) error { - ret := _m.Called(ctx, tx) - - if len(ret) == 0 { - panic("no return value specified for SimulateTransaction") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, TX) error); ok { - r0 = rf(ctx, tx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// mockRPC_SimulateTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SimulateTransaction' -type mockRPC_SimulateTransaction_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// SimulateTransaction is a helper method to define mock.On call -// - ctx context.Context -// - tx TX -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SimulateTransaction(ctx interface{}, tx interface{}) *mockRPC_SimulateTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_SimulateTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("SimulateTransaction", ctx, tx)} -} - -func (_c *mockRPC_SimulateTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, tx TX)) *mockRPC_SimulateTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(TX)) - }) - return _c -} - -func (_c *mockRPC_SimulateTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 error) *mockRPC_SimulateTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockRPC_SimulateTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, TX) error) *mockRPC_SimulateTransaction_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// SubscribeNewHead provides a mock function with given fields: ctx, channel -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SubscribeNewHead(ctx context.Context, channel chan<- HEAD) (types.Subscription, error) { - ret := _m.Called(ctx, channel) - - if len(ret) == 0 { - panic("no return value specified for SubscribeNewHead") - } - - var r0 types.Subscription - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD) (types.Subscription, error)); ok { - return rf(ctx, channel) - } - if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD) types.Subscription); ok { - r0 = rf(ctx, channel) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(types.Subscription) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, chan<- HEAD) error); ok { - r1 = rf(ctx, channel) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_SubscribeNewHead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeNewHead' -type mockRPC_SubscribeNewHead_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// SubscribeNewHead is a helper method to define mock.On call -// - ctx context.Context -// - channel chan<- HEAD -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SubscribeNewHead(ctx interface{}, channel interface{}) *mockRPC_SubscribeNewHead_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_SubscribeNewHead_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("SubscribeNewHead", ctx, channel)} -} - -func (_c *mockRPC_SubscribeNewHead_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, channel chan<- HEAD)) *mockRPC_SubscribeNewHead_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(chan<- HEAD)) - }) - return _c -} - -func (_c *mockRPC_SubscribeNewHead_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(s types.Subscription, err error) *mockRPC_SubscribeNewHead_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(s, err) - return _c -} - -func (_c *mockRPC_SubscribeNewHead_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, chan<- HEAD) (types.Subscription, error)) *mockRPC_SubscribeNewHead_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// SubscribeToFinalizedHeads provides a mock function with given fields: _a0 -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SubscribeToFinalizedHeads(_a0 context.Context) (<-chan HEAD, types.Subscription, error) { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for SubscribeToFinalizedHeads") - } - - var r0 <-chan HEAD - var r1 types.Subscription - var r2 error - if rf, ok := ret.Get(0).(func(context.Context) (<-chan HEAD, types.Subscription, error)); ok { - return rf(_a0) - } - if rf, ok := ret.Get(0).(func(context.Context) <-chan HEAD); ok { - r0 = rf(_a0) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan HEAD) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) types.Subscription); ok { - r1 = rf(_a0) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(types.Subscription) - } - } - - if rf, ok := ret.Get(2).(func(context.Context) error); ok { - r2 = rf(_a0) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// mockRPC_SubscribeToFinalizedHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToFinalizedHeads' -type mockRPC_SubscribeToFinalizedHeads_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// SubscribeToFinalizedHeads is a helper method to define mock.On call -// - _a0 context.Context -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SubscribeToFinalizedHeads(_a0 interface{}) *mockRPC_SubscribeToFinalizedHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_SubscribeToFinalizedHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("SubscribeToFinalizedHeads", _a0)} -} - -func (_c *mockRPC_SubscribeToFinalizedHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(_a0 context.Context)) *mockRPC_SubscribeToFinalizedHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPC_SubscribeToFinalizedHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 <-chan HEAD, _a1 types.Subscription, _a2 error) *mockRPC_SubscribeToFinalizedHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1, _a2) - return _c -} - -func (_c *mockRPC_SubscribeToFinalizedHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context) (<-chan HEAD, types.Subscription, error)) *mockRPC_SubscribeToFinalizedHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// SubscribeToHeads provides a mock function with given fields: ctx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SubscribeToHeads(ctx context.Context) (<-chan HEAD, types.Subscription, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for SubscribeToHeads") - } - - var r0 <-chan HEAD - var r1 types.Subscription - var r2 error - if rf, ok := ret.Get(0).(func(context.Context) (<-chan HEAD, types.Subscription, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) <-chan HEAD); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan HEAD) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) types.Subscription); ok { - r1 = rf(ctx) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(types.Subscription) - } - } - - if rf, ok := ret.Get(2).(func(context.Context) error); ok { - r2 = rf(ctx) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// mockRPC_SubscribeToHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToHeads' -type mockRPC_SubscribeToHeads_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// SubscribeToHeads is a helper method to define mock.On call -// - ctx context.Context -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SubscribeToHeads(ctx interface{}) *mockRPC_SubscribeToHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_SubscribeToHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("SubscribeToHeads", ctx)} -} - -func (_c *mockRPC_SubscribeToHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context)) *mockRPC_SubscribeToHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *mockRPC_SubscribeToHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(ch <-chan HEAD, sub types.Subscription, err error) *mockRPC_SubscribeToHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(ch, sub, err) - return _c -} - -func (_c *mockRPC_SubscribeToHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context) (<-chan HEAD, types.Subscription, error)) *mockRPC_SubscribeToHeads_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// SubscribersCount provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SubscribersCount() int32 { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for SubscribersCount") - } - - var r0 int32 - if rf, ok := ret.Get(0).(func() int32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int32) - } - - return r0 -} - -// mockRPC_SubscribersCount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribersCount' -type mockRPC_SubscribersCount_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// SubscribersCount is a helper method to define mock.On call -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SubscribersCount() *mockRPC_SubscribersCount_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_SubscribersCount_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("SubscribersCount")} -} - -func (_c *mockRPC_SubscribersCount_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func()) *mockRPC_SubscribersCount_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockRPC_SubscribersCount_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 int32) *mockRPC_SubscribersCount_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0) - return _c -} - -func (_c *mockRPC_SubscribersCount_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func() int32) *mockRPC_SubscribersCount_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// TokenBalance provides a mock function with given fields: ctx, accountAddress, tokenAddress -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) TokenBalance(ctx context.Context, accountAddress ADDR, tokenAddress ADDR) (*big.Int, error) { - ret := _m.Called(ctx, accountAddress, tokenAddress) - - if len(ret) == 0 { - panic("no return value specified for TokenBalance") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, ADDR, ADDR) (*big.Int, error)); ok { - return rf(ctx, accountAddress, tokenAddress) - } - if rf, ok := ret.Get(0).(func(context.Context, ADDR, ADDR) *big.Int); ok { - r0 = rf(ctx, accountAddress, tokenAddress) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, ADDR, ADDR) error); ok { - r1 = rf(ctx, accountAddress, tokenAddress) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_TokenBalance_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TokenBalance' -type mockRPC_TokenBalance_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// TokenBalance is a helper method to define mock.On call -// - ctx context.Context -// - accountAddress ADDR -// - tokenAddress ADDR -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) TokenBalance(ctx interface{}, accountAddress interface{}, tokenAddress interface{}) *mockRPC_TokenBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_TokenBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("TokenBalance", ctx, accountAddress, tokenAddress)} -} - -func (_c *mockRPC_TokenBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, accountAddress ADDR, tokenAddress ADDR)) *mockRPC_TokenBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(ADDR), args[2].(ADDR)) - }) - return _c -} - -func (_c *mockRPC_TokenBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 *big.Int, _a1 error) *mockRPC_TokenBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_TokenBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, ADDR, ADDR) (*big.Int, error)) *mockRPC_TokenBalance_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// TransactionByHash provides a mock function with given fields: ctx, txHash -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) TransactionByHash(ctx context.Context, txHash TX_HASH) (TX, error) { - ret := _m.Called(ctx, txHash) - - if len(ret) == 0 { - panic("no return value specified for TransactionByHash") - } - - var r0 TX - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, TX_HASH) (TX, error)); ok { - return rf(ctx, txHash) - } - if rf, ok := ret.Get(0).(func(context.Context, TX_HASH) TX); ok { - r0 = rf(ctx, txHash) - } else { - r0 = ret.Get(0).(TX) - } - - if rf, ok := ret.Get(1).(func(context.Context, TX_HASH) error); ok { - r1 = rf(ctx, txHash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_TransactionByHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TransactionByHash' -type mockRPC_TransactionByHash_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// TransactionByHash is a helper method to define mock.On call -// - ctx context.Context -// - txHash TX_HASH -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) TransactionByHash(ctx interface{}, txHash interface{}) *mockRPC_TransactionByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_TransactionByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("TransactionByHash", ctx, txHash)} -} - -func (_c *mockRPC_TransactionByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, txHash TX_HASH)) *mockRPC_TransactionByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(TX_HASH)) - }) - return _c -} - -func (_c *mockRPC_TransactionByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 TX, _a1 error) *mockRPC_TransactionByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_TransactionByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, TX_HASH) (TX, error)) *mockRPC_TransactionByHash_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// TransactionReceipt provides a mock function with given fields: ctx, txHash -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) TransactionReceipt(ctx context.Context, txHash TX_HASH) (TX_RECEIPT, error) { - ret := _m.Called(ctx, txHash) - - if len(ret) == 0 { - panic("no return value specified for TransactionReceipt") - } - - var r0 TX_RECEIPT - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, TX_HASH) (TX_RECEIPT, error)); ok { - return rf(ctx, txHash) - } - if rf, ok := ret.Get(0).(func(context.Context, TX_HASH) TX_RECEIPT); ok { - r0 = rf(ctx, txHash) - } else { - r0 = ret.Get(0).(TX_RECEIPT) - } - - if rf, ok := ret.Get(1).(func(context.Context, TX_HASH) error); ok { - r1 = rf(ctx, txHash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// mockRPC_TransactionReceipt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TransactionReceipt' -type mockRPC_TransactionReceipt_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// TransactionReceipt is a helper method to define mock.On call -// - ctx context.Context -// - txHash TX_HASH -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) TransactionReceipt(ctx interface{}, txHash interface{}) *mockRPC_TransactionReceipt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_TransactionReceipt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("TransactionReceipt", ctx, txHash)} -} - -func (_c *mockRPC_TransactionReceipt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func(ctx context.Context, txHash TX_HASH)) *mockRPC_TransactionReceipt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(TX_HASH)) - }) - return _c -} - -func (_c *mockRPC_TransactionReceipt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return(_a0 TX_RECEIPT, _a1 error) *mockRPC_TransactionReceipt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *mockRPC_TransactionReceipt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func(context.Context, TX_HASH) (TX_RECEIPT, error)) *mockRPC_TransactionReceipt_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// UnsubscribeAllExceptAliveLoop provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) UnsubscribeAllExceptAliveLoop() { - _m.Called() -} - -// mockRPC_UnsubscribeAllExceptAliveLoop_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnsubscribeAllExceptAliveLoop' -type mockRPC_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { - *mock.Call -} - -// UnsubscribeAllExceptAliveLoop is a helper method to define mock.On call -func (_e *mockRPC_Expecter[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) UnsubscribeAllExceptAliveLoop() *mockRPC_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - return &mockRPC_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{Call: _e.mock.On("UnsubscribeAllExceptAliveLoop")} -} - -func (_c *mockRPC_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Run(run func()) *mockRPC_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *mockRPC_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Return() *mockRPC_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return() - return _c -} - -func (_c *mockRPC_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) RunAndReturn(run func()) *mockRPC_UnsubscribeAllExceptAliveLoop_Call[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - _c.Call.Return(run) - return _c -} - -// newMockRPC creates a new instance of mockRPC. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func newMockRPC[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}](t interface { - mock.TestingT - Cleanup(func()) -}) *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { - mock := &mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/common/client/mock_send_only_client_test.go b/common/client/mock_send_only_client_test.go index a9c9f23b141..fa65a84551b 100644 --- a/common/client/mock_send_only_client_test.go +++ b/common/client/mock_send_only_client_test.go @@ -110,17 +110,17 @@ func (_c *mockSendOnlyClient_Close_Call[CHAIN_ID]) RunAndReturn(run func()) *moc return _c } -// DialHTTP provides a mock function with given fields: -func (_m *mockSendOnlyClient[CHAIN_ID]) DialHTTP() error { - ret := _m.Called() +// Dial provides a mock function with given fields: ctx +func (_m *mockSendOnlyClient[CHAIN_ID]) Dial(ctx context.Context) error { + ret := _m.Called(ctx) if len(ret) == 0 { - panic("no return value specified for DialHTTP") + panic("no return value specified for Dial") } var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) } else { r0 = ret.Error(0) } @@ -128,29 +128,30 @@ func (_m *mockSendOnlyClient[CHAIN_ID]) DialHTTP() error { return r0 } -// mockSendOnlyClient_DialHTTP_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DialHTTP' -type mockSendOnlyClient_DialHTTP_Call[CHAIN_ID types.ID] struct { +// mockSendOnlyClient_Dial_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Dial' +type mockSendOnlyClient_Dial_Call[CHAIN_ID types.ID] struct { *mock.Call } -// DialHTTP is a helper method to define mock.On call -func (_e *mockSendOnlyClient_Expecter[CHAIN_ID]) DialHTTP() *mockSendOnlyClient_DialHTTP_Call[CHAIN_ID] { - return &mockSendOnlyClient_DialHTTP_Call[CHAIN_ID]{Call: _e.mock.On("DialHTTP")} +// Dial is a helper method to define mock.On call +// - ctx context.Context +func (_e *mockSendOnlyClient_Expecter[CHAIN_ID]) Dial(ctx interface{}) *mockSendOnlyClient_Dial_Call[CHAIN_ID] { + return &mockSendOnlyClient_Dial_Call[CHAIN_ID]{Call: _e.mock.On("Dial", ctx)} } -func (_c *mockSendOnlyClient_DialHTTP_Call[CHAIN_ID]) Run(run func()) *mockSendOnlyClient_DialHTTP_Call[CHAIN_ID] { +func (_c *mockSendOnlyClient_Dial_Call[CHAIN_ID]) Run(run func(ctx context.Context)) *mockSendOnlyClient_Dial_Call[CHAIN_ID] { _c.Call.Run(func(args mock.Arguments) { - run() + run(args[0].(context.Context)) }) return _c } -func (_c *mockSendOnlyClient_DialHTTP_Call[CHAIN_ID]) Return(_a0 error) *mockSendOnlyClient_DialHTTP_Call[CHAIN_ID] { +func (_c *mockSendOnlyClient_Dial_Call[CHAIN_ID]) Return(_a0 error) *mockSendOnlyClient_Dial_Call[CHAIN_ID] { _c.Call.Return(_a0) return _c } -func (_c *mockSendOnlyClient_DialHTTP_Call[CHAIN_ID]) RunAndReturn(run func() error) *mockSendOnlyClient_DialHTTP_Call[CHAIN_ID] { +func (_c *mockSendOnlyClient_Dial_Call[CHAIN_ID]) RunAndReturn(run func(context.Context) error) *mockSendOnlyClient_Dial_Call[CHAIN_ID] { _c.Call.Return(run) return _c } diff --git a/common/client/mock_send_only_node_test.go b/common/client/mock_send_only_node_test.go index 53077cd49db..256faea62bc 100644 --- a/common/client/mock_send_only_node_test.go +++ b/common/client/mock_send_only_node_test.go @@ -10,11 +10,11 @@ import ( ) // mockSendOnlyNode is an autogenerated mock type for the SendOnlyNode type -type mockSendOnlyNode[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]] struct { +type mockSendOnlyNode[CHAIN_ID types.ID, RPC interface{}] struct { mock.Mock } -type mockSendOnlyNode_Expecter[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]] struct { +type mockSendOnlyNode_Expecter[CHAIN_ID types.ID, RPC interface{}] struct { mock *mock.Mock } @@ -41,7 +41,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Close() error { } // mockSendOnlyNode_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type mockSendOnlyNode_Close_Call[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]] struct { +type mockSendOnlyNode_Close_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } @@ -86,7 +86,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) ConfiguredChainID() CHAIN_ID { } // mockSendOnlyNode_ConfiguredChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConfiguredChainID' -type mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]] struct { +type mockSendOnlyNode_ConfiguredChainID_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } @@ -131,7 +131,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Name() string { } // mockSendOnlyNode_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' -type mockSendOnlyNode_Name_Call[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]] struct { +type mockSendOnlyNode_Name_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } @@ -176,7 +176,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) RPC() RPC { } // mockSendOnlyNode_RPC_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RPC' -type mockSendOnlyNode_RPC_Call[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]] struct { +type mockSendOnlyNode_RPC_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } @@ -221,7 +221,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Start(_a0 context.Context) error { } // mockSendOnlyNode_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start' -type mockSendOnlyNode_Start_Call[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]] struct { +type mockSendOnlyNode_Start_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } @@ -267,7 +267,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) State() nodeState { } // mockSendOnlyNode_State_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'State' -type mockSendOnlyNode_State_Call[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]] struct { +type mockSendOnlyNode_State_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } @@ -312,7 +312,7 @@ func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) String() string { } // mockSendOnlyNode_String_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' -type mockSendOnlyNode_String_Call[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]] struct { +type mockSendOnlyNode_String_Call[CHAIN_ID types.ID, RPC interface{}] struct { *mock.Call } @@ -340,7 +340,7 @@ func (_c *mockSendOnlyNode_String_Call[CHAIN_ID, RPC]) RunAndReturn(run func() s // newMockSendOnlyNode creates a new instance of mockSendOnlyNode. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. -func newMockSendOnlyNode[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]](t interface { +func newMockSendOnlyNode[CHAIN_ID types.ID, RPC interface{}](t interface { mock.TestingT Cleanup(func()) }) *mockSendOnlyNode[CHAIN_ID, RPC] { diff --git a/common/client/multi_node.go b/common/client/multi_node.go index 00ec436d9ab..9594743f6bd 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -4,20 +4,15 @@ import ( "context" "errors" "fmt" - "math" "math/big" - "slices" "sync" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" - - feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" ) @@ -27,11 +22,6 @@ var ( Name: "multi_node_states", Help: "The number of RPC nodes currently in the given state for the given chain", }, []string{"network", "chainId", "state"}) - // PromMultiNodeInvariantViolations reports violation of our assumptions - PromMultiNodeInvariantViolations = promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "multi_node_invariant_violations", - Help: "The number of invariant violations", - }, []string{"network", "chainId", "invariant"}) ErroringNodeError = fmt.Errorf("no live nodes available") ) @@ -39,129 +29,57 @@ var ( // It also handles multiple node RPC connections simultaneously. type MultiNode[ CHAIN_ID types.ID, - SEQ types.Sequence, - ADDR types.Hashable, - BLOCK_HASH types.Hashable, - TX any, - TX_HASH types.Hashable, - EVENT any, - EVENT_OPS any, - TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], - FEE feetypes.Fee, - HEAD types.Head[BLOCK_HASH], - RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM], - BATCH_ELEM any, -] interface { - clientAPI[ - CHAIN_ID, - SEQ, - ADDR, - BLOCK_HASH, - TX, - TX_HASH, - EVENT, - EVENT_OPS, - TX_RECEIPT, - FEE, - HEAD, - BATCH_ELEM, - ] - Close() error - NodeStates() map[string]string - SelectNodeRPC() (RPC_CLIENT, error) - - BatchCallContextAll(ctx context.Context, b []BATCH_ELEM) error - ConfiguredChainID() CHAIN_ID -} - -type multiNode[ - CHAIN_ID types.ID, - SEQ types.Sequence, - ADDR types.Hashable, - BLOCK_HASH types.Hashable, - TX any, - TX_HASH types.Hashable, - EVENT any, - EVENT_OPS any, - TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], - FEE feetypes.Fee, - HEAD types.Head[BLOCK_HASH], - RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM], - BATCH_ELEM any, + RPC any, ] struct { services.StateMachine - nodes []Node[CHAIN_ID, HEAD, RPC_CLIENT] - sendonlys []SendOnlyNode[CHAIN_ID, RPC_CLIENT] + primaryNodes []Node[CHAIN_ID, RPC] + sendOnlyNodes []SendOnlyNode[CHAIN_ID, RPC] chainID CHAIN_ID lggr logger.SugaredLogger selectionMode string - noNewHeadsThreshold time.Duration - nodeSelector NodeSelector[CHAIN_ID, HEAD, RPC_CLIENT] + nodeSelector NodeSelector[CHAIN_ID, RPC] leaseDuration time.Duration leaseTicker *time.Ticker chainFamily string reportInterval time.Duration deathDeclarationDelay time.Duration - sendTxSoftTimeout time.Duration // defines max waiting time from first response til responses evaluation activeMu sync.RWMutex - activeNode Node[CHAIN_ID, HEAD, RPC_CLIENT] + activeNode Node[CHAIN_ID, RPC] chStop services.StopChan wg sync.WaitGroup - - classifySendTxError func(tx TX, err error) SendTxReturnCode } func NewMultiNode[ CHAIN_ID types.ID, - SEQ types.Sequence, - ADDR types.Hashable, - BLOCK_HASH types.Hashable, - TX any, - TX_HASH types.Hashable, - EVENT any, - EVENT_OPS any, - TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], - FEE feetypes.Fee, - HEAD types.Head[BLOCK_HASH], - RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM], - BATCH_ELEM any, + RPC any, ]( lggr logger.Logger, - selectionMode string, - leaseDuration time.Duration, - noNewHeadsThreshold time.Duration, - nodes []Node[CHAIN_ID, HEAD, RPC_CLIENT], - sendonlys []SendOnlyNode[CHAIN_ID, RPC_CLIENT], - chainID CHAIN_ID, - chainFamily string, - classifySendTxError func(tx TX, err error) SendTxReturnCode, - sendTxSoftTimeout time.Duration, + selectionMode string, // type of the "best" RPC selector (e.g HighestHead, RoundRobin, etc.) + leaseDuration time.Duration, // defines interval on which new "best" RPC should be selected + primaryNodes []Node[CHAIN_ID, RPC], + sendOnlyNodes []SendOnlyNode[CHAIN_ID, RPC], + chainID CHAIN_ID, // configured chain ID (used to verify that passed primaryNodes belong to the same chain) + chainFamily string, // name of the chain family - used in the metrics deathDeclarationDelay time.Duration, -) MultiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM] { - nodeSelector := newNodeSelector(selectionMode, nodes) +) *MultiNode[CHAIN_ID, RPC] { + nodeSelector := newNodeSelector(selectionMode, primaryNodes) // Prometheus' default interval is 15s, set this to under 7.5s to avoid // aliasing (see: https://en.wikipedia.org/wiki/Nyquist_frequency) const reportInterval = 6500 * time.Millisecond - if sendTxSoftTimeout == 0 { - sendTxSoftTimeout = QueryTimeout / 2 - } - c := &multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]{ - nodes: nodes, - sendonlys: sendonlys, + c := &MultiNode[CHAIN_ID, RPC]{ + primaryNodes: primaryNodes, + sendOnlyNodes: sendOnlyNodes, chainID: chainID, lggr: logger.Sugared(lggr).Named("MultiNode").With("chainID", chainID.String()), selectionMode: selectionMode, - noNewHeadsThreshold: noNewHeadsThreshold, nodeSelector: nodeSelector, chStop: make(services.StopChan), leaseDuration: leaseDuration, chainFamily: chainFamily, - classifySendTxError: classifySendTxError, reportInterval: reportInterval, deathDeclarationDelay: deathDeclarationDelay, - sendTxSoftTimeout: sendTxSoftTimeout, } c.lggr.Debugf("The MultiNode is configured to use NodeSelectionMode: %s", selectionMode) @@ -169,17 +87,74 @@ func NewMultiNode[ return c } -// Dial starts every node in the pool +func (c *MultiNode[CHAIN_ID, RPC]) ChainID() CHAIN_ID { + return c.chainID +} + +func (c *MultiNode[CHAIN_ID, RPC]) DoAll(ctx context.Context, do func(ctx context.Context, rpc RPC, isSendOnly bool)) error { + var err error + ok := c.IfNotStopped(func() { + ctx, _ = c.chStop.Ctx(ctx) + + callsCompleted := 0 + for _, n := range c.primaryNodes { + select { + case <-ctx.Done(): + err = ctx.Err() + return + default: + if n.State() != nodeStateAlive { + continue + } + do(ctx, n.RPC(), false) + callsCompleted++ + } + } + if callsCompleted == 0 { + err = ErroringNodeError + } + + for _, n := range c.sendOnlyNodes { + select { + case <-ctx.Done(): + err = ctx.Err() + return + default: + if n.State() != nodeStateAlive { + continue + } + do(ctx, n.RPC(), true) + } + } + }) + if !ok { + return errors.New("MultiNode is stopped") + } + return err +} + +func (c *MultiNode[CHAIN_ID, RPC]) NodeStates() map[string]string { + states := map[string]string{} + for _, n := range c.primaryNodes { + states[n.String()] = n.State().String() + } + for _, n := range c.sendOnlyNodes { + states[n.String()] = n.State().String() + } + return states +} + +// Start starts every node in the pool // // Nodes handle their own redialing and runloops, so this function does not // return any error if the nodes aren't available -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) Dial(ctx context.Context) error { +func (c *MultiNode[CHAIN_ID, RPC]) Start(ctx context.Context) error { return c.StartOnce("MultiNode", func() (merr error) { - if len(c.nodes) == 0 { + if len(c.primaryNodes) == 0 { return fmt.Errorf("no available nodes for chain %s", c.chainID.String()) } var ms services.MultiStart - for _, n := range c.nodes { + for _, n := range c.primaryNodes { if n.ConfiguredChainID().String() != c.chainID.String() { return ms.CloseBecause(fmt.Errorf("node %s has configured chain ID %s which does not match multinode configured chain ID of %s", n.String(), n.ConfiguredChainID().String(), c.chainID.String())) } @@ -189,7 +164,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return err } } - for _, s := range c.sendonlys { + for _, s := range c.sendOnlyNodes { if s.ConfiguredChainID().String() != c.chainID.String() { return ms.CloseBecause(fmt.Errorf("sendonly node %s has configured chain ID %s which does not match multinode configured chain ID of %s", s.String(), s.ConfiguredChainID().String(), c.chainID.String())) } @@ -213,18 +188,18 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } // Close tears down the MultiNode and closes all nodes -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) Close() error { +func (c *MultiNode[CHAIN_ID, RPC]) Close() error { return c.StopOnce("MultiNode", func() error { close(c.chStop) c.wg.Wait() - return services.CloseAll(services.MultiCloser(c.nodes), services.MultiCloser(c.sendonlys)) + return services.CloseAll(services.MultiCloser(c.primaryNodes), services.MultiCloser(c.sendOnlyNodes)) }) } -// SelectNodeRPC returns an RPC of an active node. If there are no active nodes it returns an error. +// SelectRPC returns an RPC of an active node. If there are no active nodes it returns an error. // Call this method from your chain-specific client implementation to access any chain-specific rpc calls. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SelectNodeRPC() (rpc RPC_CLIENT, err error) { +func (c *MultiNode[CHAIN_ID, RPC]) SelectRPC() (rpc RPC, err error) { n, err := c.selectNode() if err != nil { return rpc, err @@ -233,7 +208,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } // selectNode returns the active Node, if it is still nodeStateAlive, otherwise it selects a new one from the NodeSelector. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) selectNode() (node Node[CHAIN_ID, HEAD, RPC_CLIENT], err error) { +func (c *MultiNode[CHAIN_ID, RPC]) selectNode() (node Node[CHAIN_ID, RPC], err error) { c.activeMu.RLock() node = c.activeNode c.activeMu.RUnlock() @@ -267,12 +242,12 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP // LatestChainInfo - returns number of live nodes available in the pool, so we can prevent the last alive node in a pool from being marked as out-of-sync. // Return highest ChainInfo most recently received by the alive nodes. // E.g. If Node A's the most recent block is 10 and highest 15 and for Node B it's - 12 and 14. This method will return 12. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) LatestChainInfo() (int, ChainInfo) { +func (c *MultiNode[CHAIN_ID, RPC]) LatestChainInfo() (int, ChainInfo) { var nLiveNodes int ch := ChainInfo{ TotalDifficulty: big.NewInt(0), } - for _, n := range c.nodes { + for _, n := range c.primaryNodes { if s, nodeChainInfo := n.StateAndLatest(); s == nodeStateAlive { nLiveNodes++ ch.BlockNumber = max(ch.BlockNumber, nodeChainInfo.BlockNumber) @@ -284,11 +259,11 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } // HighestUserObservations - returns highest ChainInfo ever observed by any user of the MultiNode -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) HighestUserObservations() ChainInfo { +func (c *MultiNode[CHAIN_ID, RPC]) HighestUserObservations() ChainInfo { ch := ChainInfo{ TotalDifficulty: big.NewInt(0), } - for _, n := range c.nodes { + for _, n := range c.primaryNodes { nodeChainInfo := n.HighestUserObservations() ch.BlockNumber = max(ch.BlockNumber, nodeChainInfo.BlockNumber) ch.FinalizedBlockNumber = max(ch.FinalizedBlockNumber, nodeChainInfo.FinalizedBlockNumber) @@ -297,12 +272,12 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return ch } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) checkLease() { +func (c *MultiNode[CHAIN_ID, RPC]) checkLease() { bestNode := c.nodeSelector.Select() - for _, n := range c.nodes { + for _, n := range c.primaryNodes { // Terminate client subscriptions. Services are responsible for reconnecting, which will be routed to the new // best node. Only terminate connections with more than 1 subscription to account for the aliveLoop subscription - if n.State() == nodeStateAlive && n != bestNode && n.SubscribersCount() > 1 { + if n.State() == nodeStateAlive && n != bestNode { c.lggr.Infof("Switching to best node from %q to %q", n.String(), bestNode.String()) n.UnsubscribeAllExceptAliveLoop() } @@ -318,7 +293,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) checkLeaseLoop() { +func (c *MultiNode[CHAIN_ID, RPC]) checkLeaseLoop() { defer c.wg.Done() c.leaseTicker = time.NewTicker(c.leaseDuration) defer c.leaseTicker.Stop() @@ -333,11 +308,11 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) runLoop() { +func (c *MultiNode[CHAIN_ID, RPC]) runLoop() { defer c.wg.Done() - nodeStates := make([]nodeWithState, len(c.nodes)) - for i, n := range c.nodes { + nodeStates := make([]nodeWithState, len(c.primaryNodes)) + for i, n := range c.primaryNodes { nodeStates[i] = nodeWithState{ Node: n.String(), State: n.State().String(), @@ -366,11 +341,11 @@ type nodeWithState struct { DeadSince *time.Time } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) report(nodesStateInfo []nodeWithState) { +func (c *MultiNode[CHAIN_ID, RPC]) report(nodesStateInfo []nodeWithState) { start := time.Now() var dead int counts := make(map[nodeState]int) - for i, n := range c.nodes { + for i, n := range c.primaryNodes { state := n.State() counts[state]++ nodesStateInfo[i].State = state.String() @@ -392,7 +367,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP PromMultiNodeRPCNodeStates.WithLabelValues(c.chainFamily, c.chainID.String(), state.String()).Set(float64(count)) } - total := len(c.nodes) + total := len(c.primaryNodes) live := total - dead c.lggr.Tracew(fmt.Sprintf("MultiNode state: %d/%d nodes are alive", live, total), "nodeStates", nodesStateInfo) if total == dead { @@ -403,462 +378,3 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP c.lggr.Errorw(fmt.Sprintf("At least one primary node is dead: %d/%d nodes are alive", live, total), "nodeStates", nodesStateInfo) } } - -// ClientAPI methods -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) BalanceAt(ctx context.Context, account ADDR, blockNumber *big.Int) (*big.Int, error) { - n, err := c.selectNode() - if err != nil { - return nil, err - } - return n.RPC().BalanceAt(ctx, account, blockNumber) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) BatchCallContext(ctx context.Context, b []BATCH_ELEM) error { - n, err := c.selectNode() - if err != nil { - return err - } - return n.RPC().BatchCallContext(ctx, b) -} - -// BatchCallContextAll calls BatchCallContext for every single node including -// sendonlys. -// CAUTION: This should only be used for mass re-transmitting transactions, it -// might have unexpected effects to use it for anything else. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) BatchCallContextAll(ctx context.Context, b []BATCH_ELEM) error { - var wg sync.WaitGroup - defer wg.Wait() - - main, selectionErr := c.selectNode() - var all []SendOnlyNode[CHAIN_ID, RPC_CLIENT] - for _, n := range c.nodes { - all = append(all, n) - } - all = append(all, c.sendonlys...) - for _, n := range all { - if n == main { - // main node is used at the end for the return value - continue - } - - if n.State() != nodeStateAlive { - continue - } - // Parallel call made to all other nodes with ignored return value - wg.Add(1) - go func(n SendOnlyNode[CHAIN_ID, RPC_CLIENT]) { - defer wg.Done() - err := n.RPC().BatchCallContext(ctx, b) - if err != nil { - c.lggr.Debugw("Secondary node BatchCallContext failed", "err", err) - } else { - c.lggr.Trace("Secondary node BatchCallContext success") - } - }(n) - } - - if selectionErr != nil { - return selectionErr - } - return main.RPC().BatchCallContext(ctx, b) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) BlockByHash(ctx context.Context, hash BLOCK_HASH) (h HEAD, err error) { - n, err := c.selectNode() - if err != nil { - return h, err - } - return n.RPC().BlockByHash(ctx, hash) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) BlockByNumber(ctx context.Context, number *big.Int) (h HEAD, err error) { - n, err := c.selectNode() - if err != nil { - return h, err - } - return n.RPC().BlockByNumber(ctx, number) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { - n, err := c.selectNode() - if err != nil { - return err - } - return n.RPC().CallContext(ctx, result, method, args...) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) CallContract( - ctx context.Context, - attempt interface{}, - blockNumber *big.Int, -) (rpcErr []byte, extractErr error) { - n, err := c.selectNode() - if err != nil { - return rpcErr, err - } - return n.RPC().CallContract(ctx, attempt, blockNumber) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) PendingCallContract( - ctx context.Context, - attempt interface{}, -) (rpcErr []byte, extractErr error) { - n, err := c.selectNode() - if err != nil { - return rpcErr, err - } - return n.RPC().PendingCallContract(ctx, attempt) -} - -// ChainID makes a direct RPC call. In most cases it should be better to use the configured chain id instead by -// calling ConfiguredChainID. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) ChainID(ctx context.Context) (id CHAIN_ID, err error) { - n, err := c.selectNode() - if err != nil { - return id, err - } - return n.RPC().ChainID(ctx) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) CodeAt(ctx context.Context, account ADDR, blockNumber *big.Int) (code []byte, err error) { - n, err := c.selectNode() - if err != nil { - return code, err - } - return n.RPC().CodeAt(ctx, account, blockNumber) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) ConfiguredChainID() CHAIN_ID { - return c.chainID -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) EstimateGas(ctx context.Context, call any) (gas uint64, err error) { - n, err := c.selectNode() - if err != nil { - return gas, err - } - return n.RPC().EstimateGas(ctx, call) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) FilterEvents(ctx context.Context, query EVENT_OPS) (e []EVENT, err error) { - n, err := c.selectNode() - if err != nil { - return e, err - } - return n.RPC().FilterEvents(ctx, query) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) LatestBlockHeight(ctx context.Context) (h *big.Int, err error) { - n, err := c.selectNode() - if err != nil { - return h, err - } - return n.RPC().LatestBlockHeight(ctx) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) LINKBalance(ctx context.Context, accountAddress ADDR, linkAddress ADDR) (b *assets.Link, err error) { - n, err := c.selectNode() - if err != nil { - return b, err - } - return n.RPC().LINKBalance(ctx, accountAddress, linkAddress) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) NodeStates() (states map[string]string) { - states = make(map[string]string) - for _, n := range c.nodes { - states[n.Name()] = n.State().String() - } - for _, s := range c.sendonlys { - states[s.Name()] = s.State().String() - } - return -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) PendingSequenceAt(ctx context.Context, addr ADDR) (s SEQ, err error) { - n, err := c.selectNode() - if err != nil { - return s, err - } - return n.RPC().PendingSequenceAt(ctx, addr) -} - -type sendTxErrors map[SendTxReturnCode][]error - -// String - returns string representation of the errors map. Required by logger to properly represent the value -func (errs sendTxErrors) String() string { - return fmt.Sprint(map[SendTxReturnCode][]error(errs)) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SendEmptyTransaction( - ctx context.Context, - newTxAttempt func(seq SEQ, feeLimit uint32, fee FEE, fromAddress ADDR) (attempt any, err error), - seq SEQ, - gasLimit uint32, - fee FEE, - fromAddress ADDR, -) (txhash string, err error) { - n, err := c.selectNode() - if err != nil { - return txhash, err - } - return n.RPC().SendEmptyTransaction(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) -} - -type sendTxResult struct { - Err error - ResultCode SendTxReturnCode -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) broadcastTxAsync(ctx context.Context, - n SendOnlyNode[CHAIN_ID, RPC_CLIENT], tx TX) sendTxResult { - txErr := n.RPC().SendTransaction(ctx, tx) - c.lggr.Debugw("Node sent transaction", "name", n.String(), "tx", tx, "err", txErr) - resultCode := c.classifySendTxError(tx, txErr) - if !slices.Contains(sendTxSuccessfulCodes, resultCode) { - c.lggr.Warnw("RPC returned error", "name", n.String(), "tx", tx, "err", txErr) - } - - return sendTxResult{Err: txErr, ResultCode: resultCode} -} - -// collectTxResults - refer to SendTransaction comment for implementation details, -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) collectTxResults(ctx context.Context, tx TX, healthyNodesNum int, txResults <-chan sendTxResult) error { - if healthyNodesNum == 0 { - return ErroringNodeError - } - // combine context and stop channel to ensure we stop, when signal received - ctx, cancel := c.chStop.Ctx(ctx) - defer cancel() - requiredResults := int(math.Ceil(float64(healthyNodesNum) * sendTxQuorum)) - errorsByCode := sendTxErrors{} - var softTimeoutChan <-chan time.Time - var resultsCount int -loop: - for { - select { - case <-ctx.Done(): - c.lggr.Debugw("Failed to collect of the results before context was done", "tx", tx, "errorsByCode", errorsByCode) - return ctx.Err() - case result := <-txResults: - errorsByCode[result.ResultCode] = append(errorsByCode[result.ResultCode], result.Err) - resultsCount++ - if slices.Contains(sendTxSuccessfulCodes, result.ResultCode) || resultsCount >= requiredResults { - break loop - } - case <-softTimeoutChan: - c.lggr.Debugw("Send Tx soft timeout expired - returning responses we've collected so far", "tx", tx, "resultsCount", resultsCount, "requiredResults", requiredResults) - break loop - } - - if softTimeoutChan == nil { - tm := time.NewTimer(c.sendTxSoftTimeout) - softTimeoutChan = tm.C - // we are fine with stopping timer at the end of function - //nolint - defer tm.Stop() - } - } - - // ignore critical error as it's reported in reportSendTxAnomalies - result, _ := aggregateTxResults(errorsByCode) - return result -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) reportSendTxAnomalies(tx TX, txResults <-chan sendTxResult) { - defer c.wg.Done() - resultsByCode := sendTxErrors{} - // txResults eventually will be closed - for txResult := range txResults { - resultsByCode[txResult.ResultCode] = append(resultsByCode[txResult.ResultCode], txResult.Err) - } - - _, criticalErr := aggregateTxResults(resultsByCode) - if criticalErr != nil { - c.lggr.Criticalw("observed invariant violation on SendTransaction", "tx", tx, "resultsByCode", resultsByCode, "err", criticalErr) - c.SvcErrBuffer.Append(criticalErr) - PromMultiNodeInvariantViolations.WithLabelValues(c.chainFamily, c.chainID.String(), criticalErr.Error()).Inc() - } -} - -func aggregateTxResults(resultsByCode sendTxErrors) (txResult error, err error) { - severeErrors, hasSevereErrors := findFirstIn(resultsByCode, sendTxSevereErrors) - successResults, hasSuccess := findFirstIn(resultsByCode, sendTxSuccessfulCodes) - if hasSuccess { - // We assume that primary node would never report false positive txResult for a transaction. - // Thus, if such case occurs it's probably due to misconfiguration or a bug and requires manual intervention. - if hasSevereErrors { - // return success, since at least 1 node has accepted our broadcasted Tx, and thus it can now be included onchain - return successResults[0], errors.New("found contradictions in nodes replies on SendTransaction: got success and severe error") - } - - // other errors are temporary - we are safe to return success - return successResults[0], nil - } - - if hasSevereErrors { - return severeErrors[0], nil - } - - // return temporary error - for _, result := range resultsByCode { - return result[0], nil - } - - err = fmt.Errorf("expected at least one response on SendTransaction") - return err, err -} - -const sendTxQuorum = 0.7 - -// SendTransaction - broadcasts transaction to all the send-only and primary nodes regardless of their health. -// A returned nil or error does not guarantee that the transaction will or won't be included. Additional checks must be -// performed to determine the final state. -// -// Send-only nodes' results are ignored as they tend to return false-positive responses. Broadcast to them is necessary -// to speed up the propagation of TX in the network. -// -// Handling of primary nodes' results consists of collection and aggregation. -// In the collection step, we gather as many results as possible while minimizing waiting time. This operation succeeds -// on one of the following conditions: -// * Received at least one success -// * Received at least one result and `sendTxSoftTimeout` expired -// * Received results from the sufficient number of nodes defined by sendTxQuorum. -// The aggregation is based on the following conditions: -// * If there is at least one success - returns success -// * If there is at least one terminal error - returns terminal error -// * If there is both success and terminal error - returns success and reports invariant violation -// * Otherwise, returns any (effectively random) of the errors. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SendTransaction(ctx context.Context, tx TX) error { - if len(c.nodes) == 0 { - return ErroringNodeError - } - - healthyNodesNum := 0 - txResults := make(chan sendTxResult, len(c.nodes)) - // Must wrap inside IfNotStopped to avoid waitgroup racing with Close - ok := c.IfNotStopped(func() { - // fire-n-forget, as sendOnlyNodes can not be trusted with result reporting - for _, n := range c.sendonlys { - if n.State() != nodeStateAlive { - continue - } - c.wg.Add(1) - go func(n SendOnlyNode[CHAIN_ID, RPC_CLIENT]) { - defer c.wg.Done() - c.broadcastTxAsync(ctx, n, tx) - }(n) - } - - var primaryBroadcastWg sync.WaitGroup - txResultsToReport := make(chan sendTxResult, len(c.nodes)) - for _, n := range c.nodes { - if n.State() != nodeStateAlive { - continue - } - - healthyNodesNum++ - primaryBroadcastWg.Add(1) - go func(n SendOnlyNode[CHAIN_ID, RPC_CLIENT]) { - defer primaryBroadcastWg.Done() - result := c.broadcastTxAsync(ctx, n, tx) - // both channels are sufficiently buffered, so we won't be locked - txResultsToReport <- result - txResults <- result - }(n) - } - - c.wg.Add(1) - go func() { - // wait for primary nodes to finish the broadcast before closing the channel - primaryBroadcastWg.Wait() - close(txResultsToReport) - close(txResults) - c.wg.Done() - }() - - c.wg.Add(1) - go c.reportSendTxAnomalies(tx, txResultsToReport) - }) - if !ok { - return fmt.Errorf("aborted while broadcasting tx - multiNode is stopped: %w", context.Canceled) - } - - return c.collectTxResults(ctx, tx, healthyNodesNum, txResults) -} - -// findFirstIn - returns first existing value for the slice of keys -func findFirstIn[K comparable, V any](set map[K]V, keys []K) (V, bool) { - for _, k := range keys { - if v, ok := set[k]; ok { - return v, true - } - } - var v V - return v, false -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SequenceAt(ctx context.Context, account ADDR, blockNumber *big.Int) (s SEQ, err error) { - n, err := c.selectNode() - if err != nil { - return s, err - } - return n.RPC().SequenceAt(ctx, account, blockNumber) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SimulateTransaction(ctx context.Context, tx TX) error { - n, err := c.selectNode() - if err != nil { - return err - } - return n.RPC().SimulateTransaction(ctx, tx) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SubscribeNewHead(ctx context.Context, channel chan<- HEAD) (s types.Subscription, err error) { - n, err := c.selectNode() - if err != nil { - return s, err - } - return n.RPC().SubscribeNewHead(ctx, channel) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SubscribeToHeads(ctx context.Context) (ch <-chan HEAD, sub types.Subscription, err error) { - n, err := c.selectNode() - if err != nil { - return nil, nil, err - } - return n.RPC().SubscribeToHeads(ctx) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) TokenBalance(ctx context.Context, account ADDR, tokenAddr ADDR) (b *big.Int, err error) { - n, err := c.selectNode() - if err != nil { - return b, err - } - return n.RPC().TokenBalance(ctx, account, tokenAddr) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) TransactionByHash(ctx context.Context, txHash TX_HASH) (tx TX, err error) { - n, err := c.selectNode() - if err != nil { - return tx, err - } - return n.RPC().TransactionByHash(ctx, txHash) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) TransactionReceipt(ctx context.Context, txHash TX_HASH) (txr TX_RECEIPT, err error) { - n, err := c.selectNode() - if err != nil { - return txr, err - } - return n.RPC().TransactionReceipt(ctx, txHash) -} - -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) LatestFinalizedBlock(ctx context.Context) (head HEAD, err error) { - n, err := c.selectNode() - if err != nil { - return head, err - } - - return n.RPC().LatestFinalizedBlock(ctx) -} diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go index ffef0c29d56..57b849a3c0a 100644 --- a/common/client/multi_node_test.go +++ b/common/client/multi_node_test.go @@ -1,44 +1,38 @@ package client import ( - "context" - "errors" "fmt" "math/big" "math/rand" "testing" "time" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "go.uber.org/zap" - "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/common/types" ) -type multiNodeRPCClient RPC[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], any] +type multiNodeRPCClient RPCClient[types.ID, types.Head[Hashable]] type testMultiNode struct { - *multiNode[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient, any] + *MultiNode[types.ID, multiNodeRPCClient] } type multiNodeOpts struct { logger logger.Logger selectionMode string leaseDuration time.Duration - noNewHeadsThreshold time.Duration - nodes []Node[types.ID, types.Head[Hashable], multiNodeRPCClient] + nodes []Node[types.ID, multiNodeRPCClient] sendonlys []SendOnlyNode[types.ID, multiNodeRPCClient] chainID types.ID chainFamily string - classifySendTxError func(tx any, err error) SendTxReturnCode - sendTxSoftTimeout time.Duration deathDeclarationDelay time.Duration } @@ -47,46 +41,32 @@ func newTestMultiNode(t *testing.T, opts multiNodeOpts) testMultiNode { opts.logger = logger.Test(t) } - result := NewMultiNode[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient, any](opts.logger, - opts.selectionMode, opts.leaseDuration, opts.noNewHeadsThreshold, opts.nodes, opts.sendonlys, - opts.chainID, opts.chainFamily, opts.classifySendTxError, opts.sendTxSoftTimeout, opts.deathDeclarationDelay) + result := NewMultiNode[types.ID, multiNodeRPCClient]( + opts.logger, opts.selectionMode, opts.leaseDuration, opts.nodes, opts.sendonlys, opts.chainID, opts.chainFamily, opts.deathDeclarationDelay) return testMultiNode{ - result.(*multiNode[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient, any]), + result, } } -func newMultiNodeRPCClient(t *testing.T) *mockRPC[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], any] { - return newMockRPC[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], any](t) -} - -func newHealthyNode(t *testing.T, chainID types.ID) *mockNode[types.ID, types.Head[Hashable], multiNodeRPCClient] { +func newHealthyNode(t *testing.T, chainID types.ID) *mockNode[types.ID, multiNodeRPCClient] { return newNodeWithState(t, chainID, nodeStateAlive) } -func newNodeWithState(t *testing.T, chainID types.ID, state nodeState) *mockNode[types.ID, types.Head[Hashable], multiNodeRPCClient] { - node := newDialableNode(t, chainID) - node.On("State").Return(state).Maybe() - return node -} - -func newDialableNode(t *testing.T, chainID types.ID) *mockNode[types.ID, types.Head[Hashable], multiNodeRPCClient] { - node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) +func newNodeWithState(t *testing.T, chainID types.ID, state nodeState) *mockNode[types.ID, multiNodeRPCClient] { + node := newMockNode[types.ID, multiNodeRPCClient](t) node.On("ConfiguredChainID").Return(chainID).Once() node.On("Start", mock.Anything).Return(nil).Once() node.On("Close").Return(nil).Once() node.On("String").Return(fmt.Sprintf("healthy_node_%d", rand.Int())).Maybe() node.On("SetPoolChainInfoProvider", mock.Anything).Once() + node.On("State").Return(state).Maybe() return node } func TestMultiNode_Dial(t *testing.T) { t.Parallel() - newMockNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient] + newMockNode := newMockNode[types.ID, multiNodeRPCClient] newMockSendOnlyNode := newMockSendOnlyNode[types.ID, multiNodeRPCClient] t.Run("Fails without nodes", func(t *testing.T) { @@ -95,7 +75,7 @@ func TestMultiNode_Dial(t *testing.T) { selectionMode: NodeSelectionModeRoundRobin, chainID: types.RandomID(), }) - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) assert.EqualError(t, err, fmt.Sprintf("no available nodes for chain %s", mn.chainID.String())) }) t.Run("Fails with wrong node's chainID", func(t *testing.T) { @@ -109,9 +89,9 @@ func TestMultiNode_Dial(t *testing.T) { mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: multiNodeChainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + nodes: []Node[types.ID, multiNodeRPCClient]{node}, }) - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) assert.EqualError(t, err, fmt.Sprintf("node %s has configured chain ID %s which does not match multinode configured chain ID of %s", nodeName, nodeChainID, mn.chainID)) }) t.Run("Fails if node fails", func(t *testing.T) { @@ -119,15 +99,15 @@ func TestMultiNode_Dial(t *testing.T) { node := newMockNode(t) chainID := types.RandomID() node.On("ConfiguredChainID").Return(chainID).Once() - node.On("SetPoolChainInfoProvider", mock.Anything).Once() expectedError := errors.New("failed to start node") node.On("Start", mock.Anything).Return(expectedError).Once() + node.On("SetPoolChainInfoProvider", mock.Anything).Once() mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + nodes: []Node[types.ID, multiNodeRPCClient]{node}, }) - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) assert.EqualError(t, err, expectedError.Error()) }) @@ -137,16 +117,16 @@ func TestMultiNode_Dial(t *testing.T) { node1 := newHealthyNode(t, chainID) node2 := newMockNode(t) node2.On("ConfiguredChainID").Return(chainID).Once() - node2.On("SetPoolChainInfoProvider", mock.Anything).Once() expectedError := errors.New("failed to start node") node2.On("Start", mock.Anything).Return(expectedError).Once() + node2.On("SetPoolChainInfoProvider", mock.Anything).Once() mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node1, node2}, + nodes: []Node[types.ID, multiNodeRPCClient]{node1, node2}, }) - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) assert.EqualError(t, err, expectedError.Error()) }) t.Run("Fails with wrong send only node's chainID", func(t *testing.T) { @@ -162,10 +142,10 @@ func TestMultiNode_Dial(t *testing.T) { mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: multiNodeChainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + nodes: []Node[types.ID, multiNodeRPCClient]{node}, sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{sendOnly}, }) - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) assert.EqualError(t, err, fmt.Sprintf("sendonly node %s has configured chain ID %s which does not match multinode configured chain ID of %s", sendOnlyName, sendOnlyChainID, mn.chainID)) }) @@ -189,10 +169,10 @@ func TestMultiNode_Dial(t *testing.T) { mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + nodes: []Node[types.ID, multiNodeRPCClient]{node}, sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{sendOnly1, sendOnly2}, }) - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) assert.EqualError(t, err, expectedError.Error()) }) t.Run("Starts successfully with healthy nodes", func(t *testing.T) { @@ -202,11 +182,11 @@ func TestMultiNode_Dial(t *testing.T) { mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + nodes: []Node[types.ID, multiNodeRPCClient]{node}, sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{newHealthySendOnly(t, chainID)}, }) defer func() { assert.NoError(t, mn.Close()) }() - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) require.NoError(t, err) selectedNode, err := mn.selectNode() require.NoError(t, err) @@ -225,13 +205,13 @@ func TestMultiNode_Report(t *testing.T) { mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node1, node2}, + nodes: []Node[types.ID, multiNodeRPCClient]{node1, node2}, logger: lggr, }) mn.reportInterval = tests.TestInterval mn.deathDeclarationDelay = tests.TestInterval defer func() { assert.NoError(t, mn.Close()) }() - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) require.NoError(t, err) tests.AssertLogCountEventually(t, observedLogs, "At least one primary node is dead: 1/2 nodes are alive", 2) }) @@ -243,13 +223,13 @@ func TestMultiNode_Report(t *testing.T) { mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + nodes: []Node[types.ID, multiNodeRPCClient]{node}, logger: lggr, }) mn.reportInterval = tests.TestInterval mn.deathDeclarationDelay = tests.TestInterval defer func() { assert.NoError(t, mn.Close()) }() - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) require.NoError(t, err) tests.AssertLogCountEventually(t, observedLogs, "no primary nodes available: 0/1 nodes are alive", 2) err = mn.Healthy() @@ -269,10 +249,10 @@ func TestMultiNode_CheckLease(t *testing.T) { selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, logger: lggr, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + nodes: []Node[types.ID, multiNodeRPCClient]{node}, }) defer func() { assert.NoError(t, mn.Close()) }() - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) require.NoError(t, err) tests.RequireLogMessage(t, observedLogs, "Best node switching is disabled") }) @@ -285,11 +265,11 @@ func TestMultiNode_CheckLease(t *testing.T) { selectionMode: NodeSelectionModeHighestHead, chainID: chainID, logger: lggr, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + nodes: []Node[types.ID, multiNodeRPCClient]{node}, leaseDuration: 0, }) defer func() { assert.NoError(t, mn.Close()) }() - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) require.NoError(t, err) tests.RequireLogMessage(t, observedLogs, "Best node switching is disabled") }) @@ -297,22 +277,21 @@ func TestMultiNode_CheckLease(t *testing.T) { t.Parallel() chainID := types.RandomID() node := newHealthyNode(t, chainID) - node.On("SubscribersCount").Return(int32(2)) node.On("UnsubscribeAllExceptAliveLoop") bestNode := newHealthyNode(t, chainID) - nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + nodeSelector := newMockNodeSelector[types.ID, multiNodeRPCClient](t) nodeSelector.On("Select").Return(bestNode) lggr, observedLogs := logger.TestObserved(t, zap.InfoLevel) mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeHighestHead, chainID: chainID, logger: lggr, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node, bestNode}, + nodes: []Node[types.ID, multiNodeRPCClient]{node, bestNode}, leaseDuration: tests.TestInterval, }) defer func() { assert.NoError(t, mn.Close()) }() mn.nodeSelector = nodeSelector - err := mn.Dial(tests.Context(t)) + err := mn.Start(tests.Context(t)) require.NoError(t, err) tests.AssertLogEventually(t, observedLogs, fmt.Sprintf("Switching to best node from %q to %q", node.String(), bestNode.String())) tests.AssertEventually(t, func() bool { @@ -338,15 +317,15 @@ func TestMultiNode_CheckLease(t *testing.T) { expectedResult := map[string]string{} for name, state := range nodes { - node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - node.On("Name").Return(name).Once() + node := newMockNode[types.ID, multiNodeRPCClient](t) node.On("State").Return(state).Once() + node.On("String").Return(name).Once() opts.nodes = append(opts.nodes, node) sendOnly := newMockSendOnlyNode[types.ID, multiNodeRPCClient](t) sendOnlyName := "send_only_" + name - sendOnly.On("Name").Return(sendOnlyName).Once() sendOnly.On("State").Return(state).Once() + sendOnly.On("String").Return(sendOnlyName).Once() opts.sendonlys = append(opts.sendonlys, sendOnly) expectedResult[name] = state.String() @@ -364,17 +343,17 @@ func TestMultiNode_selectNode(t *testing.T) { t.Run("Returns same node, if it's still healthy", func(t *testing.T) { t.Parallel() chainID := types.RandomID() - node1 := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + node1 := newMockNode[types.ID, multiNodeRPCClient](t) node1.On("State").Return(nodeStateAlive).Once() node1.On("String").Return("node1").Maybe() - node2 := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + node2 := newMockNode[types.ID, multiNodeRPCClient](t) node2.On("String").Return("node2").Maybe() mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node1, node2}, + nodes: []Node[types.ID, multiNodeRPCClient]{node1, node2}, }) - nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + nodeSelector := newMockNodeSelector[types.ID, multiNodeRPCClient](t) nodeSelector.On("Select").Return(node1).Once() mn.nodeSelector = nodeSelector prevActiveNode, err := mn.selectNode() @@ -387,17 +366,17 @@ func TestMultiNode_selectNode(t *testing.T) { t.Run("Updates node if active is not healthy", func(t *testing.T) { t.Parallel() chainID := types.RandomID() - oldBest := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + oldBest := newMockNode[types.ID, multiNodeRPCClient](t) oldBest.On("String").Return("oldBest").Maybe() - oldBest.On("UnsubscribeAllExceptAliveLoop").Once() - newBest := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + oldBest.On("UnsubscribeAllExceptAliveLoop") + newBest := newMockNode[types.ID, multiNodeRPCClient](t) newBest.On("String").Return("newBest").Maybe() mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{oldBest, newBest}, + nodes: []Node[types.ID, multiNodeRPCClient]{oldBest, newBest}, }) - nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + nodeSelector := newMockNodeSelector[types.ID, multiNodeRPCClient](t) nodeSelector.On("Select").Return(oldBest).Once() mn.nodeSelector = nodeSelector activeNode, err := mn.selectNode() @@ -419,7 +398,7 @@ func TestMultiNode_selectNode(t *testing.T) { chainID: chainID, logger: lggr, }) - nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + nodeSelector := newMockNodeSelector[types.ID, multiNodeRPCClient](t) nodeSelector.On("Select").Return(nil).Once() nodeSelector.On("Name").Return("MockedNodeSelector").Once() mn.nodeSelector = nodeSelector @@ -532,10 +511,10 @@ func TestMultiNode_ChainInfo(t *testing.T) { tc := testCases[i] t.Run(tc.Name, func(t *testing.T) { for _, params := range tc.NodeParams { - node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + node := newMockNode[types.ID, multiNodeRPCClient](t) + mn.primaryNodes = append(mn.primaryNodes, node) node.On("StateAndLatest").Return(params.State, params.LatestChainInfo) node.On("HighestUserObservations").Return(params.HighestUserObservations) - mn.nodes = append(mn.nodes, node) } nNodes, latestChainInfo := mn.LatestChainInfo() @@ -547,400 +526,3 @@ func TestMultiNode_ChainInfo(t *testing.T) { }) } } - -func TestMultiNode_BatchCallContextAll(t *testing.T) { - t.Parallel() - t.Run("Fails if failed to select active node", func(t *testing.T) { - chainID := types.RandomID() - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - }) - nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - nodeSelector.On("Select").Return(nil).Once() - nodeSelector.On("Name").Return("MockedNodeSelector").Once() - mn.nodeSelector = nodeSelector - err := mn.BatchCallContextAll(tests.Context(t), nil) - require.EqualError(t, err, ErroringNodeError.Error()) - }) - t.Run("Returns error if RPC call fails for active node", func(t *testing.T) { - chainID := types.RandomID() - rpc := newMultiNodeRPCClient(t) - expectedError := errors.New("rpc failed to do the batch call") - rpc.On("BatchCallContext", mock.Anything, mock.Anything).Return(expectedError).Once() - node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - node.On("RPC").Return(rpc) - nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - nodeSelector.On("Select").Return(node).Once() - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - }) - mn.nodeSelector = nodeSelector - err := mn.BatchCallContextAll(tests.Context(t), nil) - require.EqualError(t, err, expectedError.Error()) - }) - t.Run("Waits for all nodes to complete the call and logs results", func(t *testing.T) { - // setup RPCs - failedRPC := newMultiNodeRPCClient(t) - failedRPC.On("BatchCallContext", mock.Anything, mock.Anything). - Return(errors.New("rpc failed to do the batch call")).Once() - okRPC := newMultiNodeRPCClient(t) - okRPC.On("BatchCallContext", mock.Anything, mock.Anything).Return(nil).Twice() - - // setup ok and failed auxiliary nodes - okNode := newMockSendOnlyNode[types.ID, multiNodeRPCClient](t) - okNode.On("RPC").Return(okRPC).Once() - okNode.On("State").Return(nodeStateAlive) - failedNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - failedNode.On("RPC").Return(failedRPC).Once() - failedNode.On("State").Return(nodeStateAlive) - - // setup main node - mainNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - mainNode.On("RPC").Return(okRPC) - nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - nodeSelector.On("Select").Return(mainNode).Once() - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: types.RandomID(), - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{failedNode, mainNode}, - sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{okNode}, - logger: lggr, - }) - mn.nodeSelector = nodeSelector - - err := mn.BatchCallContextAll(tests.Context(t), nil) - require.NoError(t, err) - tests.RequireLogMessage(t, observedLogs, "Secondary node BatchCallContext failed") - }) - t.Run("Does not call BatchCallContext for unhealthy nodes", func(t *testing.T) { - // setup RPCs - okRPC := newMultiNodeRPCClient(t) - okRPC.On("BatchCallContext", mock.Anything, mock.Anything).Return(nil).Twice() - - // setup ok and failed auxiliary nodes - healthyNode := newMockSendOnlyNode[types.ID, multiNodeRPCClient](t) - healthyNode.On("RPC").Return(okRPC).Once() - healthyNode.On("State").Return(nodeStateAlive) - deadNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - deadNode.On("State").Return(nodeStateUnreachable) - - // setup main node - mainNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - mainNode.On("RPC").Return(okRPC) - nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - nodeSelector.On("Select").Return(mainNode).Once() - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: types.RandomID(), - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{deadNode, mainNode}, - sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{healthyNode, deadNode}, - }) - mn.nodeSelector = nodeSelector - - err := mn.BatchCallContextAll(tests.Context(t), nil) - require.NoError(t, err) - }) -} - -func TestMultiNode_SendTransaction(t *testing.T) { - t.Parallel() - classifySendTxError := func(tx any, err error) SendTxReturnCode { - if err != nil { - return Fatal - } - - return Successful - } - newNodeWithState := func(t *testing.T, state nodeState, txErr error, sendTxRun func(args mock.Arguments)) *mockNode[types.ID, types.Head[Hashable], multiNodeRPCClient] { - rpc := newMultiNodeRPCClient(t) - rpc.On("SendTransaction", mock.Anything, mock.Anything).Return(txErr).Run(sendTxRun).Maybe() - node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) - node.On("String").Return("node name").Maybe() - node.On("RPC").Return(rpc).Maybe() - node.On("State").Return(state).Maybe() - node.On("Close").Return(nil).Once() - return node - } - - newNode := func(t *testing.T, txErr error, sendTxRun func(args mock.Arguments)) *mockNode[types.ID, types.Head[Hashable], multiNodeRPCClient] { - return newNodeWithState(t, nodeStateAlive, txErr, sendTxRun) - } - newStartedMultiNode := func(t *testing.T, opts multiNodeOpts) testMultiNode { - mn := newTestMultiNode(t, opts) - err := mn.StartOnce("startedTestMultiNode", func() error { return nil }) - require.NoError(t, err) - t.Cleanup(func() { - require.NoError(t, mn.Close()) - }) - return mn - } - t.Run("Fails if there is no nodes available", func(t *testing.T) { - mn := newStartedMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: types.RandomID(), - }) - err := mn.SendTransaction(tests.Context(t), nil) - assert.EqualError(t, err, ErroringNodeError.Error()) - }) - t.Run("Transaction failure happy path", func(t *testing.T) { - chainID := types.RandomID() - expectedError := errors.New("transaction failed") - mainNode := newNode(t, expectedError, nil) - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - mn := newStartedMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{mainNode}, - sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{newNode(t, errors.New("unexpected error"), nil)}, - classifySendTxError: classifySendTxError, - logger: lggr, - }) - err := mn.SendTransaction(tests.Context(t), nil) - require.EqualError(t, err, expectedError.Error()) - tests.AssertLogCountEventually(t, observedLogs, "Node sent transaction", 2) - tests.AssertLogCountEventually(t, observedLogs, "RPC returned error", 2) - }) - t.Run("Transaction success happy path", func(t *testing.T) { - chainID := types.RandomID() - mainNode := newNode(t, nil, nil) - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - mn := newStartedMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{mainNode}, - sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{newNode(t, errors.New("unexpected error"), nil)}, - classifySendTxError: classifySendTxError, - logger: lggr, - }) - err := mn.SendTransaction(tests.Context(t), nil) - require.NoError(t, err) - tests.AssertLogCountEventually(t, observedLogs, "Node sent transaction", 2) - tests.AssertLogCountEventually(t, observedLogs, "RPC returned error", 1) - }) - t.Run("Context expired before collecting sufficient results", func(t *testing.T) { - chainID := types.RandomID() - testContext, testCancel := context.WithCancel(tests.Context(t)) - defer testCancel() - mainNode := newNode(t, errors.New("unexpected error"), func(_ mock.Arguments) { - // block caller til end of the test - <-testContext.Done() - }) - mn := newStartedMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{mainNode}, - classifySendTxError: classifySendTxError, - }) - requestContext, cancel := context.WithCancel(tests.Context(t)) - cancel() - err := mn.SendTransaction(requestContext, nil) - require.EqualError(t, err, "context canceled") - }) - t.Run("Soft timeout stops results collection", func(t *testing.T) { - chainID := types.RandomID() - expectedError := errors.New("tmp error") - fastNode := newNode(t, expectedError, nil) - // hold reply from the node till end of the test - testContext, testCancel := context.WithCancel(tests.Context(t)) - defer testCancel() - slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) { - // block caller til end of the test - <-testContext.Done() - }) - mn := newStartedMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{fastNode, slowNode}, - classifySendTxError: classifySendTxError, - sendTxSoftTimeout: tests.TestInterval, - }) - err := mn.SendTransaction(tests.Context(t), nil) - require.EqualError(t, err, expectedError.Error()) - }) - t.Run("Returns success without waiting for the rest of the nodes", func(t *testing.T) { - chainID := types.RandomID() - fastNode := newNode(t, nil, nil) - // hold reply from the node till end of the test - testContext, testCancel := context.WithCancel(tests.Context(t)) - defer testCancel() - slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) { - // block caller til end of the test - <-testContext.Done() - }) - slowSendOnly := newNode(t, errors.New("send only failed"), func(_ mock.Arguments) { - // block caller til end of the test - <-testContext.Done() - }) - lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) - mn := newTestMultiNode(t, multiNodeOpts{ - logger: lggr, - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{fastNode, slowNode}, - sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{slowSendOnly}, - classifySendTxError: classifySendTxError, - sendTxSoftTimeout: tests.TestInterval, - }) - assert.NoError(t, mn.StartOnce("startedTestMultiNode", func() error { return nil })) - err := mn.SendTransaction(tests.Context(t), nil) - require.NoError(t, err) - testCancel() - require.NoError(t, mn.Close()) - tests.AssertLogEventually(t, observedLogs, "observed invariant violation on SendTransaction") - }) - t.Run("Fails when closed", func(t *testing.T) { - mn := newTestMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: types.RandomID(), - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{newNode(t, nil, nil)}, - sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{newNode(t, nil, nil)}, - classifySendTxError: classifySendTxError, - }) - err := mn.StartOnce("startedTestMultiNode", func() error { return nil }) - require.NoError(t, err) - require.NoError(t, mn.Close()) - err = mn.SendTransaction(tests.Context(t), nil) - require.EqualError(t, err, "aborted while broadcasting tx - multiNode is stopped: context canceled") - }) - t.Run("Returns error if there is no healthy primary nodes", func(t *testing.T) { - mn := newStartedMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: types.RandomID(), - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{newNodeWithState(t, nodeStateUnreachable, nil, nil)}, - sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{newNodeWithState(t, nodeStateUnreachable, nil, nil)}, - classifySendTxError: classifySendTxError, - }) - err := mn.SendTransaction(tests.Context(t), nil) - assert.EqualError(t, err, ErroringNodeError.Error()) - }) - t.Run("Transaction success even if one of the nodes is unhealthy", func(t *testing.T) { - chainID := types.RandomID() - mainNode := newNode(t, nil, nil) - unexpectedCall := func(args mock.Arguments) { - panic("SendTx must not be called for unhealthy node") - } - unhealthyNode := newNodeWithState(t, nodeStateUnreachable, nil, unexpectedCall) - unhealthySendOnlyNode := newNodeWithState(t, nodeStateUnreachable, nil, unexpectedCall) - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - mn := newStartedMultiNode(t, multiNodeOpts{ - selectionMode: NodeSelectionModeRoundRobin, - chainID: chainID, - nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{mainNode, unhealthyNode}, - sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{unhealthySendOnlyNode, newNode(t, errors.New("unexpected error"), nil)}, - classifySendTxError: classifySendTxError, - logger: lggr, - }) - err := mn.SendTransaction(tests.Context(t), nil) - require.NoError(t, err) - tests.AssertLogCountEventually(t, observedLogs, "Node sent transaction", 2) - tests.AssertLogCountEventually(t, observedLogs, "RPC returned error", 1) - }) -} - -func TestMultiNode_SendTransaction_aggregateTxResults(t *testing.T) { - t.Parallel() - // ensure failure on new SendTxReturnCode - codesToCover := map[SendTxReturnCode]struct{}{} - for code := Successful; code < sendTxReturnCodeLen; code++ { - codesToCover[code] = struct{}{} - } - - testCases := []struct { - Name string - ExpectedTxResult string - ExpectedCriticalErr string - ResultsByCode sendTxErrors - }{ - { - Name: "Returns success and logs critical error on success and Fatal", - ExpectedTxResult: "success", - ExpectedCriticalErr: "found contradictions in nodes replies on SendTransaction: got success and severe error", - ResultsByCode: sendTxErrors{ - Successful: {errors.New("success")}, - Fatal: {errors.New("fatal")}, - }, - }, - { - Name: "Returns TransactionAlreadyKnown and logs critical error on TransactionAlreadyKnown and Fatal", - ExpectedTxResult: "tx_already_known", - ExpectedCriticalErr: "found contradictions in nodes replies on SendTransaction: got success and severe error", - ResultsByCode: sendTxErrors{ - TransactionAlreadyKnown: {errors.New("tx_already_known")}, - Unsupported: {errors.New("unsupported")}, - }, - }, - { - Name: "Prefers sever error to temporary", - ExpectedTxResult: "underpriced", - ExpectedCriticalErr: "", - ResultsByCode: sendTxErrors{ - Retryable: {errors.New("retryable")}, - Underpriced: {errors.New("underpriced")}, - }, - }, - { - Name: "Returns temporary error", - ExpectedTxResult: "retryable", - ExpectedCriticalErr: "", - ResultsByCode: sendTxErrors{ - Retryable: {errors.New("retryable")}, - }, - }, - { - Name: "Insufficient funds is treated as error", - ExpectedTxResult: "", - ExpectedCriticalErr: "", - ResultsByCode: sendTxErrors{ - Successful: {nil}, - InsufficientFunds: {errors.New("insufficientFunds")}, - }, - }, - { - Name: "Logs critical error on empty ResultsByCode", - ExpectedTxResult: "expected at least one response on SendTransaction", - ExpectedCriticalErr: "expected at least one response on SendTransaction", - ResultsByCode: sendTxErrors{}, - }, - { - Name: "Zk out of counter error", - ExpectedTxResult: "not enough keccak counters to continue the execution", - ExpectedCriticalErr: "", - ResultsByCode: sendTxErrors{ - TerminallyStuck: {errors.New("not enough keccak counters to continue the execution")}, - }, - }, - } - - for _, testCase := range testCases { - for code := range testCase.ResultsByCode { - delete(codesToCover, code) - } - t.Run(testCase.Name, func(t *testing.T) { - txResult, err := aggregateTxResults(testCase.ResultsByCode) - if testCase.ExpectedTxResult == "" { - assert.NoError(t, err) - } else { - assert.EqualError(t, txResult, testCase.ExpectedTxResult) - } - - logger.Sugared(logger.Test(t)).Info("Map: " + fmt.Sprint(testCase.ResultsByCode)) - logger.Sugared(logger.Test(t)).Criticalw("observed invariant violation on SendTransaction", "resultsByCode", testCase.ResultsByCode, "err", err) - - if testCase.ExpectedCriticalErr == "" { - assert.NoError(t, err) - } else { - assert.EqualError(t, err, testCase.ExpectedCriticalErr) - } - }) - } - - // explicitly signal that following codes are properly handled in aggregateTxResults, - //but dedicated test cases won't be beneficial - for _, codeToIgnore := range []SendTxReturnCode{Unknown, ExceedsMaxFee, FeeOutOfValidRange} { - delete(codesToCover, codeToIgnore) - } - assert.Empty(t, codesToCover, "all of the SendTxReturnCode must be covered by this test") -} diff --git a/common/client/node.go b/common/client/node.go index 7885fe76760..66161ac5d5f 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -58,8 +58,7 @@ type ChainConfig interface { type Node[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], + RPC any, ] interface { // State returns most accurate state of the Node on the moment of call. // While some of the checks may be performed in the background and State may return cached value, critical, like @@ -72,13 +71,15 @@ type Node[ SetPoolChainInfoProvider(PoolChainInfoProvider) // Name is a unique identifier for this node. Name() string + // String - returns string representation of the node, useful for debugging (name + URLS used to connect to the RPC) String() string RPC() RPC - SubscribersCount() int32 // UnsubscribeAllExceptAliveLoop - closes all subscriptions except the aliveLoop subscription UnsubscribeAllExceptAliveLoop() ConfiguredChainID() CHAIN_ID + // Order - returns priority order configured for the RPC Order() int32 + // Start - starts health checks Start(context.Context) error Close() error } @@ -86,7 +87,7 @@ type Node[ type node[ CHAIN_ID types.ID, HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], + RPC RPCClient[CHAIN_ID, HEAD], ] struct { services.StateMachine lfcLog logger.Logger @@ -111,12 +112,14 @@ type node[ stopCh services.StopChan // wg waits for subsidiary goroutines wg sync.WaitGroup + + healthCheckSubs []types.Subscription } func NewNode[ CHAIN_ID types.ID, HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], + RPC RPCClient[CHAIN_ID, HEAD], ]( nodeCfg NodeConfig, chainCfg ChainConfig, @@ -129,7 +132,7 @@ func NewNode[ nodeOrder int32, rpc RPC, chainFamily string, -) Node[CHAIN_ID, HEAD, RPC] { +) Node[CHAIN_ID, RPC] { n := new(node[CHAIN_ID, HEAD, RPC]) n.name = name n.id = id @@ -181,12 +184,16 @@ func (n *node[CHAIN_ID, HEAD, RPC]) RPC() RPC { return n.rpc } -func (n *node[CHAIN_ID, HEAD, RPC]) SubscribersCount() int32 { - return n.rpc.SubscribersCount() +// unsubscribeAllExceptAliveLoop is not thread-safe; it should only be called +// while holding the stateMu lock. +func (n *node[CHAIN_ID, HEAD, RPC]) unsubscribeAllExceptAliveLoop() { + n.rpc.UnsubscribeAllExcept(n.healthCheckSubs...) } func (n *node[CHAIN_ID, HEAD, RPC]) UnsubscribeAllExceptAliveLoop() { - n.rpc.UnsubscribeAllExceptAliveLoop() + n.stateMu.Lock() + defer n.stateMu.Unlock() + n.unsubscribeAllExceptAliveLoop() } func (n *node[CHAIN_ID, HEAD, RPC]) Close() error { @@ -318,13 +325,6 @@ func (n *node[CHAIN_ID, HEAD, RPC]) verifyConn(ctx context.Context, lggr logger. return nodeStateAlive } -// disconnectAll disconnects all clients connected to the node -// WARNING: NOT THREAD-SAFE -// This must be called from within the n.stateMu lock -func (n *node[CHAIN_ID, HEAD, RPC]) disconnectAll() { - n.rpc.DisconnectAll() -} - func (n *node[CHAIN_ID, HEAD, RPC]) Order() int32 { return n.order } diff --git a/common/client/node_fsm.go b/common/client/node_fsm.go index e58de071fbc..4a80e4fae9b 100644 --- a/common/client/node_fsm.go +++ b/common/client/node_fsm.go @@ -256,7 +256,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToOutOfSync(fn func()) { } switch n.state { case nodeStateAlive: - n.disconnectAll() + n.rpc.Close() n.state = nodeStateOutOfSync default: panic(transitionFail(n.state, nodeStateOutOfSync)) @@ -281,7 +281,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToUnreachable(fn func()) { } switch n.state { case nodeStateUndialed, nodeStateDialed, nodeStateAlive, nodeStateOutOfSync, nodeStateInvalidChainID, nodeStateSyncing: - n.disconnectAll() + n.rpc.Close() n.state = nodeStateUnreachable default: panic(transitionFail(n.state, nodeStateUnreachable)) @@ -324,7 +324,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToInvalidChainID(fn func()) { } switch n.state { case nodeStateDialed, nodeStateOutOfSync, nodeStateSyncing: - n.disconnectAll() + n.rpc.Close() n.state = nodeStateInvalidChainID default: panic(transitionFail(n.state, nodeStateInvalidChainID)) @@ -349,7 +349,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToSyncing(fn func()) { } switch n.state { case nodeStateDialed, nodeStateOutOfSync, nodeStateInvalidChainID: - n.disconnectAll() + n.rpc.Close() n.state = nodeStateSyncing default: panic(transitionFail(n.state, nodeStateSyncing)) diff --git a/common/client/node_fsm_test.go b/common/client/node_fsm_test.go index dc0ca0e7de8..93460d934a3 100644 --- a/common/client/node_fsm_test.go +++ b/common/client/node_fsm_test.go @@ -39,47 +39,47 @@ func TestUnit_Node_StateTransitions(t *testing.T) { t.Run("transitionToAlive", func(t *testing.T) { const destinationState = nodeStateAlive allowedStates := []nodeState{nodeStateDialed, nodeStateInvalidChainID, nodeStateSyncing} - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) testTransition(t, rpc, testNode.transitionToAlive, destinationState, allowedStates...) }) t.Run("transitionToInSync", func(t *testing.T) { const destinationState = nodeStateAlive allowedStates := []nodeState{nodeStateOutOfSync, nodeStateSyncing} - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) testTransition(t, rpc, testNode.transitionToInSync, destinationState, allowedStates...) }) t.Run("transitionToOutOfSync", func(t *testing.T) { const destinationState = nodeStateOutOfSync allowedStates := []nodeState{nodeStateAlive} - rpc := newMockNodeClient[types.ID, Head](t) - rpc.On("DisconnectAll").Once() + rpc := newMockRPCClient[types.ID, Head](t) + rpc.On("Close") testTransition(t, rpc, testNode.transitionToOutOfSync, destinationState, allowedStates...) }) t.Run("transitionToUnreachable", func(t *testing.T) { const destinationState = nodeStateUnreachable allowedStates := []nodeState{nodeStateUndialed, nodeStateDialed, nodeStateAlive, nodeStateOutOfSync, nodeStateInvalidChainID, nodeStateSyncing} - rpc := newMockNodeClient[types.ID, Head](t) - rpc.On("DisconnectAll").Times(len(allowedStates)) + rpc := newMockRPCClient[types.ID, Head](t) + rpc.On("Close") testTransition(t, rpc, testNode.transitionToUnreachable, destinationState, allowedStates...) }) t.Run("transitionToInvalidChain", func(t *testing.T) { const destinationState = nodeStateInvalidChainID allowedStates := []nodeState{nodeStateDialed, nodeStateOutOfSync, nodeStateSyncing} - rpc := newMockNodeClient[types.ID, Head](t) - rpc.On("DisconnectAll").Times(len(allowedStates)) + rpc := newMockRPCClient[types.ID, Head](t) + rpc.On("Close") testTransition(t, rpc, testNode.transitionToInvalidChainID, destinationState, allowedStates...) }) t.Run("transitionToSyncing", func(t *testing.T) { const destinationState = nodeStateSyncing allowedStates := []nodeState{nodeStateDialed, nodeStateOutOfSync, nodeStateInvalidChainID} - rpc := newMockNodeClient[types.ID, Head](t) - rpc.On("DisconnectAll").Times(len(allowedStates)) + rpc := newMockRPCClient[types.ID, Head](t) + rpc.On("Close") testTransition(t, rpc, testNode.transitionToSyncing, destinationState, allowedStates...) }) t.Run("transitionToSyncing panics if nodeIsSyncing is disabled", func(t *testing.T) { - rpc := newMockNodeClient[types.ID, Head](t) - rpc.On("DisconnectAll").Once() + rpc := newMockRPCClient[types.ID, Head](t) + rpc.On("Close") node := newTestNode(t, testNodeOpts{rpc: rpc}) node.setState(nodeStateDialed) fn := new(fnMock) @@ -90,7 +90,7 @@ func TestUnit_Node_StateTransitions(t *testing.T) { }) } -func testTransition(t *testing.T, rpc *mockNodeClient[types.ID, Head], transition func(node testNode, fn func()), destinationState nodeState, allowedStates ...nodeState) { +func testTransition(t *testing.T, rpc *mockRPCClient[types.ID, Head], transition func(node testNode, fn func()), destinationState nodeState, allowedStates ...nodeState) { node := newTestNode(t, testNodeOpts{rpc: rpc, config: testNodeConfig{nodeIsSyncingEnabled: true}}) for _, allowedState := range allowedStates { m := new(fnMock) diff --git a/common/client/node_lifecycle.go b/common/client/node_lifecycle.go index 40d9a9ef6ef..ce508a43dde 100644 --- a/common/client/node_lifecycle.go +++ b/common/client/node_lifecycle.go @@ -103,23 +103,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { return } - // TODO: will be removed as part of merging effort with BCI-2875 - n.rpc.SetAliveLoopSub(headsSub.sub) - - defer headsSub.Unsubscribe() - - var finalizedHeadsSub headSubscription[HEAD] - if n.chainCfg.FinalityTagEnabled() { - finalizedHeadsSub, err = n.registerNewSubscription(ctx, lggr.With("subscriptionType", "finalizedHeads"), - n.chainCfg.NoNewFinalizedHeadsThreshold(), n.rpc.SubscribeToFinalizedHeads) - if err != nil { - lggr.Errorw("Failed to subscribe to finalized heads", "err", err) - n.declareUnreachable() - return - } - - defer finalizedHeadsSub.Unsubscribe() - } + defer n.unsubscribeHealthChecks() var pollCh <-chan time.Time if pollInterval > 0 { @@ -137,6 +121,17 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { lggr.Debug("Polling disabled") } + var finalizedHeadsSub headSubscription[HEAD] + if n.chainCfg.FinalityTagEnabled() { + finalizedHeadsSub, err = n.registerNewSubscription(ctx, lggr.With("subscriptionType", "finalizedHeads"), + n.chainCfg.NoNewFinalizedHeadsThreshold(), n.rpc.SubscribeToFinalizedHeads) + if err != nil { + lggr.Errorw("Failed to subscribe to finalized heads", "err", err) + n.declareUnreachable() + return + } + } + localHighestChainInfo, _ := n.rpc.GetInterceptedChainInfo() var pollFailures uint32 @@ -146,13 +141,10 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { return case <-pollCh: promPoolRPCNodePolls.WithLabelValues(n.chainID.String(), n.name).Inc() - lggr.Tracew("Polling for version", "nodeState", n.getCachedState(), "pollFailures", pollFailures) - var version string - version, err = func(ctx context.Context) (string, error) { - ctx, cancel := context.WithTimeout(ctx, pollInterval) - defer cancel() - return n.RPC().ClientVersion(ctx) - }(ctx) + lggr.Tracew("Pinging RPC", "nodeState", n.State(), "pollFailures", pollFailures) + pollCtx, cancel := context.WithTimeout(ctx, pollInterval) + err = n.RPC().Ping(pollCtx) + cancel() if err != nil { // prevent overflow if pollFailures < math.MaxUint32 { @@ -161,7 +153,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { } lggr.Warnw(fmt.Sprintf("Poll failure, RPC endpoint %s failed to respond properly", n.String()), "err", err, "pollFailures", pollFailures, "nodeState", n.getCachedState()) } else { - lggr.Debugw("Version poll successful", "nodeState", n.getCachedState(), "clientVersion", version) + lggr.Debugw("Ping successful", "nodeState", n.State()) promPoolRPCNodePollsSuccess.WithLabelValues(n.chainID.String(), n.name).Inc() pollFailures = 0 } @@ -193,7 +185,6 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { n.declareUnreachable() return } - receivedNewHead := n.onNewHead(lggr, &localHighestChainInfo, bh) if receivedNewHead && noNewHeadsTimeoutThreshold > 0 { headsSub.ResetTimer(noNewHeadsTimeoutThreshold) @@ -219,7 +210,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { return case latestFinalized, open := <-finalizedHeadsSub.Heads: if !open { - lggr.Errorw("Finalized heads subscription channel unexpectedly closed", "nodeState", n.getCachedState()) + lggr.Errorw("Finalized heads subscription channel unexpectedly closed") n.declareUnreachable() return } @@ -251,6 +242,15 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { } } +func (n *node[CHAIN_ID, HEAD, RPC]) unsubscribeHealthChecks() { + n.stateMu.Lock() + for _, sub := range n.healthCheckSubs { + sub.Unsubscribe() + } + n.healthCheckSubs = []types.Subscription{} + n.stateMu.Unlock() +} + type headSubscription[HEAD any] struct { Heads <-chan HEAD Errors <-chan error @@ -284,11 +284,10 @@ func (n *node[CHAIN_ID, HEAD, PRC]) registerNewSubscription(ctx context.Context, result.Errors = sub.Err() lggr.Debug("Successfully subscribed") - // TODO: will be removed as part of merging effort with BCI-2875 result.sub = sub - //n.stateMu.Lock() - //n.healthCheckSubs = append(n.healthCheckSubs, sub) - //n.stateMu.Unlock() + n.stateMu.Lock() + n.healthCheckSubs = append(n.healthCheckSubs, sub) + n.stateMu.Unlock() result.cleanUpTasks = append(result.cleanUpTasks, sub.Unsubscribe) @@ -350,6 +349,12 @@ func (n *node[CHAIN_ID, HEAD, RPC]) onNewHead(lggr logger.SugaredLogger, chainIn return true } +const ( + msgReceivedBlock = "Received block for RPC node, waiting until back in-sync to mark as live again" + msgReceivedFinalizedBlock = "Received new finalized block for RPC node, waiting until back in-sync to mark as live again" + msgInSync = "RPC node back in sync" +) + // isOutOfSyncWithPool returns outOfSync true if num or td is more than SyncThresold behind the best node. // Always returns outOfSync false for SyncThreshold 0. // liveNodes is only included when outOfSync is true. @@ -376,12 +381,6 @@ func (n *node[CHAIN_ID, HEAD, RPC]) isOutOfSyncWithPool(localState ChainInfo) (o } } -const ( - msgReceivedBlock = "Received block for RPC node, waiting until back in-sync to mark as live again" - msgReceivedFinalizedBlock = "Received new finalized block for RPC node, waiting until back in-sync to mark as live again" - msgInSync = "RPC node back in sync" -) - // outOfSyncLoop takes an OutOfSync node and waits until isOutOfSync returns false to go back to live status func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(syncIssues syncStatus) { defer n.wg.Done() @@ -422,8 +421,9 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(syncIssues syncStatus) { return } + defer n.unsubscribeHealthChecks() + lggr.Tracew("Successfully subscribed to heads feed on out-of-sync RPC node") - defer headsSub.Unsubscribe() noNewFinalizedBlocksTimeoutThreshold := n.chainCfg.NoNewFinalizedHeadsThreshold() var finalizedHeadsSub headSubscription[HEAD] @@ -437,7 +437,6 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(syncIssues syncStatus) { } lggr.Tracew("Successfully subscribed to finalized heads feed on out-of-sync RPC node") - defer finalizedHeadsSub.Unsubscribe() } _, localHighestChainInfo := n.rpc.GetInterceptedChainInfo() diff --git a/common/client/node_lifecycle_test.go b/common/client/node_lifecycle_test.go index 833bccf7f29..6f9b4653393 100644 --- a/common/client/node_lifecycle_test.go +++ b/common/client/node_lifecycle_test.go @@ -23,12 +23,19 @@ import ( "github.com/smartcontractkit/chainlink/v2/common/types/mocks" ) +func newSub(t *testing.T) *mocks.Subscription { + sub := mocks.NewSubscription(t) + sub.On("Err").Return((<-chan error)(nil)).Maybe() + sub.On("Unsubscribe") + return sub +} + func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { t.Parallel() newDialedNode := func(t *testing.T, opts testNodeOpts) testNode { node := newTestNode(t, opts) - opts.rpc.On("Close").Return(nil).Once() + opts.rpc.On("Close").Return(nil) node.setState(nodeStateDialed) return node @@ -42,14 +49,13 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("if initial subscribe fails, transitions to unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) node := newDialedNode(t, testNodeOpts{ rpc: rpc, }) defer func() { assert.NoError(t, node.close()) }() expectedError := errors.New("failed to subscribe to rpc") - rpc.On("DisconnectAll").Once() rpc.On("SubscribeToHeads", mock.Anything).Return(nil, nil, expectedError).Once() // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() @@ -60,7 +66,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("if remote RPC connection is closed transitions to unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) node := newDialedNode(t, testNodeOpts{ @@ -76,9 +82,6 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { sub.On("Err").Return((<-chan error)(errChan)).Once() sub.On("Unsubscribe").Once() rpc.On("SubscribeToHeads", mock.Anything).Return(nil, sub, nil).Once() - rpc.On("SetAliveLoopSub", sub).Once() - // disconnects all on transfer to unreachable - rpc.On("DisconnectAll").Once() // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() @@ -87,16 +90,13 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) newSubscribedNode := func(t *testing.T, opts testNodeOpts) testNode { - sub := mocks.NewSubscription(t) - sub.On("Err").Return((<-chan error)(nil)) - sub.On("Unsubscribe").Once() - opts.rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil) - opts.rpc.On("SetAliveLoopSub", sub).Once() + sub := newSub(t) + opts.rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once() return newDialedNode(t, opts) } t.Run("Stays alive and waits for signal", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newSubscribedNode(t, testNodeOpts{ @@ -112,7 +112,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("stays alive while below pollFailureThreshold and resets counter on success", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}) lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) const pollFailureThreshold = 3 @@ -128,17 +128,17 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { pollError := errors.New("failed to get ClientVersion") // 1. Return error several times, but below threshold - rpc.On("ClientVersion", mock.Anything).Return("", pollError).Run(func(_ mock.Arguments) { + rpc.On("Ping", mock.Anything).Return(pollError).Run(func(_ mock.Arguments) { // stays healthy while below threshold assert.Equal(t, nodeStateAlive, node.State()) }).Times(pollFailureThreshold - 1) // 2. Successful call that is expected to reset counter - rpc.On("ClientVersion", mock.Anything).Return("client_version", nil).Once() + rpc.On("Ping", mock.Anything).Return(nil).Once() // 3. Return error. If we have not reset the timer, we'll transition to nonAliveState - rpc.On("ClientVersion", mock.Anything).Return("", pollError).Once() + rpc.On("Ping", mock.Anything).Return(pollError).Once() // 4. Once during the call, check if node is alive var ensuredAlive atomic.Bool - rpc.On("ClientVersion", mock.Anything).Return("client_version", nil).Run(func(_ mock.Arguments) { + rpc.On("Ping", mock.Anything).Return(nil).Run(func(_ mock.Arguments) { if ensuredAlive.Load() { return } @@ -146,15 +146,15 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { assert.Equal(t, nodeStateAlive, node.State()) }).Once() // redundant call to stay in alive state - rpc.On("ClientVersion", mock.Anything).Return("client_version", nil) + rpc.On("Ping", mock.Anything).Return(nil) node.declareAlive() tests.AssertLogCountEventually(t, observedLogs, fmt.Sprintf("Poll failure, RPC endpoint %s failed to respond properly", node.String()), pollFailureThreshold) - tests.AssertLogCountEventually(t, observedLogs, "Version poll successful", 2) + tests.AssertLogCountEventually(t, observedLogs, "Ping successful", 2) assert.True(t, ensuredAlive.Load(), "expected to ensure that node was alive") }) t.Run("with threshold poll failures, transitions to unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}) lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) const pollFailureThreshold = 3 @@ -168,10 +168,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() pollError := errors.New("failed to get ClientVersion") - rpc.On("ClientVersion", mock.Anything).Return("", pollError) - // disconnects all on transfer to unreachable - rpc.On("DisconnectAll").Once() - // might be called in unreachable loop + rpc.On("Ping", mock.Anything).Return(pollError) rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() tests.AssertLogCountEventually(t, observedLogs, fmt.Sprintf("Poll failure, RPC endpoint %s failed to respond properly", node.String()), pollFailureThreshold) @@ -181,7 +178,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("with threshold poll failures, but we are the last node alive, forcibly keeps it alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) const pollFailureThreshold = 3 node := newSubscribedNode(t, testNodeOpts{ @@ -200,14 +197,14 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { node.SetPoolChainInfoProvider(poolInfo) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: 20}, ChainInfo{BlockNumber: 20}) pollError := errors.New("failed to get ClientVersion") - rpc.On("ClientVersion", mock.Anything).Return("", pollError) + rpc.On("Ping", mock.Anything).Return(pollError) node.declareAlive() tests.AssertLogEventually(t, observedLogs, fmt.Sprintf("RPC endpoint failed to respond to %d consecutive polls", pollFailureThreshold)) assert.Equal(t, nodeStateAlive, node.State()) }) t.Run("when behind more than SyncThreshold, transitions to out of sync", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) const syncThreshold = 10 node := newSubscribedNode(t, testNodeOpts{ @@ -220,6 +217,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() + rpc.On("Ping", mock.Anything).Return(nil) const mostRecentBlock = 20 rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: mostRecentBlock}, ChainInfo{BlockNumber: 30}) poolInfo := newMockPoolChainInfoProvider(t) @@ -228,14 +226,11 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { TotalDifficulty: big.NewInt(10), }).Once() node.SetPoolChainInfoProvider(poolInfo) - rpc.On("ClientVersion", mock.Anything).Return("", nil) // tries to redial in outOfSync rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Run(func(_ mock.Arguments) { assert.Equal(t, nodeStateOutOfSync, node.State()) }).Once() - // disconnects all on transfer to unreachable or outOfSync - rpc.On("DisconnectAll").Maybe() - // might be called in unreachable loop + rpc.On("Close").Maybe() rpc.On("Dial", mock.Anything).Run(func(_ mock.Arguments) { require.Equal(t, nodeStateOutOfSync, node.State()) }).Return(errors.New("failed to dial")).Maybe() @@ -244,7 +239,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("when behind more than SyncThreshold but we are the last live node, forcibly stays alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) const syncThreshold = 10 node := newSubscribedNode(t, testNodeOpts{ @@ -257,6 +252,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() + rpc.On("Ping", mock.Anything).Return(nil) const mostRecentBlock = 20 rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: mostRecentBlock}, ChainInfo{BlockNumber: 30}) poolInfo := newMockPoolChainInfoProvider(t) @@ -265,13 +261,12 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { TotalDifficulty: big.NewInt(10), }).Once() node.SetPoolChainInfoProvider(poolInfo) - rpc.On("ClientVersion", mock.Anything).Return("", nil) node.declareAlive() tests.AssertLogEventually(t, observedLogs, fmt.Sprintf("RPC endpoint has fallen behind; %s %s", msgCannotDisable, msgDegradedState)) }) t.Run("when behind but SyncThreshold=0, stay alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newSubscribedNode(t, testNodeOpts{ config: testNodeConfig{ @@ -283,16 +278,16 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() + rpc.On("Ping", mock.Anything).Return(nil) const mostRecentBlock = 20 rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: mostRecentBlock}, ChainInfo{BlockNumber: 30}) - rpc.On("ClientVersion", mock.Anything).Return("", nil) node.declareAlive() - tests.AssertLogCountEventually(t, observedLogs, "Version poll successful", 2) + tests.AssertLogCountEventually(t, observedLogs, "Ping successful", 2) assert.Equal(t, nodeStateAlive, node.State()) }) t.Run("when no new heads received for threshold, transitions to out of sync", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() node := newSubscribedNode(t, testNodeOpts{ config: testNodeConfig{}, @@ -306,9 +301,6 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Run(func(_ mock.Arguments) { assert.Equal(t, nodeStateOutOfSync, node.State()) }).Once() - // disconnects all on transfer to unreachable or outOfSync - rpc.On("DisconnectAll").Maybe() - // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() tests.AssertEventually(t, func() bool { @@ -319,7 +311,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("when no new heads received for threshold but we are the last live node, forcibly stays alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newSubscribedNode(t, testNodeOpts{ @@ -341,21 +333,16 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { tests.AssertLogEventually(t, observedLogs, fmt.Sprintf("RPC endpoint detected out of sync; %s %s", msgCannotDisable, msgDegradedState)) assert.Equal(t, nodeStateAlive, node.State()) }) - newSub := func(t *testing.T) *mocks.Subscription { - sub := mocks.NewSubscription(t) - sub.On("Err").Return((<-chan error)(nil)) - sub.On("Unsubscribe").Once() - return sub - } + t.Run("rpc closed head channel", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) + sub := newSub(t) + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() ch := make(chan Head) rpc.On("SubscribeToHeads", mock.Anything).Run(func(args mock.Arguments) { close(ch) - }).Return((<-chan Head)(ch), newSub(t), nil).Once() - rpc.On("SetAliveLoopSub", mock.Anything).Once() - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() + }).Return((<-chan Head)(ch), sub, nil).Once() lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) node := newDialedNode(t, testNodeOpts{ lggr: lggr, @@ -366,9 +353,6 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { rpc: rpc, }) defer func() { assert.NoError(t, node.close()) }() - // disconnects all on transfer to unreachable or outOfSync - rpc.On("DisconnectAll").Once() - // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() tests.AssertLogEventually(t, observedLogs, "Subscription channel unexpectedly closed") @@ -376,19 +360,16 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("If finality tag is not enabled updates finalized block metric using finality depth and latest head", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) - sub := mocks.NewSubscription(t) - sub.On("Err").Return((<-chan error)(nil)) - sub.On("Unsubscribe").Once() + rpc := newMockRPCClient[types.ID, Head](t) + sub := newSub(t) const blockNumber = 1000 const finalityDepth = 10 const expectedBlock = 990 - rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() ch := make(chan Head) rpc.On("SubscribeToHeads", mock.Anything).Run(func(args mock.Arguments) { + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() go writeHeads(t, ch, head{BlockNumber: blockNumber - 1}, head{BlockNumber: blockNumber}, head{BlockNumber: blockNumber - 1}) }).Return((<-chan Head)(ch), sub, nil).Once() - rpc.On("SetAliveLoopSub", sub).Once() name := "node-" + rand.Str(5) node := newDialedNode(t, testNodeOpts{ config: testNodeConfig{}, @@ -407,13 +388,15 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { return float64(expectedBlock) == m.Gauge.GetValue() }) }) - t.Run("If fails to subscribe to latest finalized blocks, transitions to unreachable ", func(t *testing.T) { + t.Run("If fails to subscribe to latest finalized blocks, transitions to unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) + sub := newSub(t) + rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once() expectedError := errors.New("failed to subscribe to finalized heads") - rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return(nil, mocks.NewSubscription(t), expectedError).Once() - lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - node := newSubscribedNode(t, testNodeOpts{ + rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return(nil, sub, expectedError).Once() + lggr, _ := logger.TestObserved(t, zap.DebugLevel) + node := newDialedNode(t, testNodeOpts{ config: testNodeConfig{ finalizedBlockPollInterval: tests.TestInterval, }, @@ -424,28 +407,24 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - // disconnects all on transfer to unreachable or outOfSync - rpc.On("DisconnectAll").Once() - // might be called in unreachable loop - rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() - tests.AssertLogEventually(t, observedLogs, "Failed to subscribe to finalized heads") tests.AssertEventually(t, func() bool { - return nodeStateUnreachable == node.State() + return node.State() == nodeStateUnreachable }) }) t.Run("Logs warning if latest finalized block is not valid", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) + sub := newSub(t) + rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once() ch := make(chan Head, 1) head := newMockHead(t) head.On("IsValid").Return(false) rpc.On("SubscribeToFinalizedHeads", mock.Anything).Run(func(args mock.Arguments) { ch <- head - }).Return((<-chan Head)(ch), newSub(t), nil).Once() + }).Return((<-chan Head)(ch), sub, nil).Once() + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() - rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), newSub(t), nil).Once() - rpc.On("SetAliveLoopSub", mock.Anything).Once() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newDialedNode(t, testNodeOpts{ config: testNodeConfig{}, @@ -461,7 +440,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("On new finalized block updates corresponding metric", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) const expectedBlock = 1101 const finalityDepth = 10 ch := make(chan Head) @@ -496,7 +475,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("If finalized heads channel is closed, transitions to unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() ch := make(chan Head) close(ch) @@ -510,9 +489,6 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - // disconnects all on transfer to unreachable or outOfSync - rpc.On("DisconnectAll").Once() - // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() tests.AssertLogEventually(t, observedLogs, "Finalized heads subscription channel unexpectedly closed") @@ -522,7 +498,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("when no new finalized heads received for threshold, transitions to out of sync", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() ch := make(chan Head, 1) ch <- head{BlockNumber: 10}.ToMockHead(t) @@ -543,9 +519,6 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Run(func(_ mock.Arguments) { assert.Equal(t, nodeStateOutOfSync, node.State()) }).Once() - // disconnects all on transfer to unreachable or outOfSync - rpc.On("DisconnectAll").Maybe() - // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() tests.AssertLogEventually(t, observed, fmt.Sprintf("RPC's finalized state is out of sync; no new finalized heads received for %s (last finalized head received was 10)", noNewFinalizedHeadsThreshold)) @@ -557,7 +530,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("when no new finalized heads received for threshold but we are the last live node, forcibly stays alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return(make(<-chan Head), newSub(t), nil).Once() lggr, observed := logger.TestObserved(t, zap.DebugLevel) @@ -584,8 +557,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("If finalized subscription returns an error, transitions to unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) - rpc.On("DisconnectAll").Once() + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() sub := mocks.NewSubscription(t) errCh := make(chan error, 1) @@ -602,8 +574,6 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - // disconnects all on transfer to unreachable or outOfSync - // might be called in unreachable loop rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() node.declareAlive() tests.AssertLogEventually(t, observedLogs, "Finalized heads subscription was terminated") @@ -637,7 +607,7 @@ func writeHeads(t *testing.T, ch chan<- Head, heads ...head) { } } -func setupRPCForAliveLoop(t *testing.T, rpc *mockNodeClient[types.ID, Head]) { +func setupRPCForAliveLoop(t *testing.T, rpc *mockRPCClient[types.ID, Head]) { rpc.On("Dial", mock.Anything).Return(nil).Maybe() aliveSubscription := mocks.NewSubscription(t) aliveSubscription.On("Err").Return(nil).Maybe() @@ -653,9 +623,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { newAliveNode := func(t *testing.T, opts testNodeOpts) testNode { node := newTestNode(t, opts) - opts.rpc.On("Close").Return(nil).Once() - // disconnects all on transfer to unreachable or outOfSync - opts.rpc.On("DisconnectAll") + opts.rpc.On("Close").Return(nil) node.setState(nodeStateAlive) return node } @@ -669,7 +637,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("on old blocks stays outOfSync and returns on close", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr := logger.Test(t) node := newAliveNode(t, testNodeOpts{ @@ -681,6 +649,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{BlockNumber: 0}, ChainInfo{BlockNumber: 13}).Once() outOfSyncSubscription := mocks.NewSubscription(t) @@ -706,7 +675,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("if initial dial fails, transitions to unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) node := newAliveNode(t, testNodeOpts{ rpc: rpc, }) @@ -715,6 +684,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { expectedError := errors.New("failed to dial rpc") // might be called again in unreachable loop, so no need to set once rpc.On("Dial", mock.Anything).Return(expectedError) + node.declareOutOfSync(syncStatusNoNewHead) tests.AssertEventually(t, func() bool { return node.State() == nodeStateUnreachable @@ -722,7 +692,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("if fail to get chainID, transitions to unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) node := newAliveNode(t, testNodeOpts{ rpc: rpc, }) @@ -732,6 +702,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { rpc.On("Dial", mock.Anything).Return(nil).Once() // for unreachable rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() + expectedError := errors.New("failed to get chain ID") // might be called multiple times rpc.On("ChainID", mock.Anything).Return(types.NewIDFromInt(0), expectedError) @@ -742,7 +713,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("if chainID does not match, transitions to invalidChainID", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) rpcChainID := types.NewIDFromInt(11) node := newAliveNode(t, testNodeOpts{ @@ -753,6 +724,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { // one for out-of-sync & one for invalid chainID rpc.On("Dial", mock.Anything).Return(nil).Twice() + // might be called multiple times rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) node.declareOutOfSync(syncStatusNoNewHead) @@ -762,7 +734,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("if syncing, transitions to syncing", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) node := newAliveNode(t, testNodeOpts{ rpc: rpc, @@ -773,6 +745,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { rpc.On("Dial", mock.Anything).Return(nil) rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) + // might be called multiple times rpc.On("IsSyncing", mock.Anything).Return(true, nil) node.declareOutOfSync(syncStatusNoNewHead) @@ -782,7 +755,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("if fails to fetch syncing status, transitions to unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) node := newAliveNode(t, testNodeOpts{ rpc: rpc, @@ -793,6 +766,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { // one for out-of-sync rpc.On("Dial", mock.Anything).Return(nil).Once() + // for unreachable rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() @@ -805,7 +779,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("if fails to subscribe, becomes unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() node := newAliveNode(t, testNodeOpts{ rpc: rpc, @@ -817,6 +791,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() expectedError := errors.New("failed to subscribe") rpc.On("SubscribeToHeads", mock.Anything).Return(nil, nil, expectedError).Once() + rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe() node.declareOutOfSync(syncStatusNoNewHead) tests.AssertEventually(t, func() bool { @@ -825,7 +800,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("on subscription termination becomes unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) node := newAliveNode(t, testNodeOpts{ @@ -837,6 +812,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() sub := mocks.NewSubscription(t) errChan := make(chan error, 1) @@ -853,7 +829,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("becomes unreachable if head channel is closed", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) node := newAliveNode(t, testNodeOpts{ @@ -865,11 +841,10 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() + rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() - sub := mocks.NewSubscription(t) - sub.On("Err").Return((<-chan error)(nil)) - sub.On("Unsubscribe").Once() + sub := newSub(t) ch := make(chan Head) rpc.On("SubscribeToHeads", mock.Anything).Run(func(args mock.Arguments) { close(ch) @@ -883,7 +858,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("becomes alive if it receives a newer head", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newAliveNode(t, testNodeOpts{ @@ -916,7 +891,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("becomes alive if there is no other nodes", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newAliveNode(t, testNodeOpts{ @@ -953,7 +928,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("Stays out-of-sync if received new head, but lags behind pool", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newAliveNode(t, testNodeOpts{ @@ -998,13 +973,11 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) // creates RPC mock with all calls necessary to create heads subscription that won't produce any events - newRPCWithNoOpHeads := func(t *testing.T, chainID types.ID) *mockNodeClient[types.ID, Head] { - rpc := newMockNodeClient[types.ID, Head](t) + newRPCWithNoOpHeads := func(t *testing.T, chainID types.ID) *mockRPCClient[types.ID, Head] { + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("ChainID", mock.Anything).Return(chainID, nil).Once() - sub := mocks.NewSubscription(t) - sub.On("Err").Return((<-chan error)(nil)) - sub.On("Unsubscribe").Once() + sub := newSub(t) rpc.On("SubscribeToHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once() return rpc } @@ -1053,6 +1026,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { sub.On("Unsubscribe").Once() rpc.On("SubscribeToFinalizedHeads", mock.Anything).Return(make(<-chan Head), sub, nil).Once() rpc.On("GetInterceptedChainInfo").Return(ChainInfo{}, ChainInfo{}).Once() + // unreachable rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe() node.declareOutOfSync(syncStatusNoNewHead) @@ -1076,9 +1050,8 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() - sub := mocks.NewSubscription(t) - sub.On("Err").Return((<-chan error)(nil)) - sub.On("Unsubscribe").Once() + sub := newSub(t) + ch := make(chan Head) rpc.On("SubscribeToFinalizedHeads", mock.Anything).Run(func(args mock.Arguments) { close(ch) @@ -1168,9 +1141,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { newAliveNode := func(t *testing.T, opts testNodeOpts) testNode { node := newTestNode(t, opts) - opts.rpc.On("Close").Return(nil).Once() - // disconnects all on transfer to unreachable - opts.rpc.On("DisconnectAll") + opts.rpc.On("Close").Return(nil) node.setState(nodeStateAlive) return node @@ -1184,7 +1155,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { }) t.Run("on failed redial, keeps trying", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newAliveNode(t, testNodeOpts{ @@ -1200,7 +1171,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { }) t.Run("on failed chainID verification, keep trying", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newAliveNode(t, testNodeOpts{ @@ -1219,7 +1190,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { }) t.Run("on chain ID mismatch transitions to invalidChainID", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) rpcChainID := types.NewIDFromInt(11) node := newAliveNode(t, testNodeOpts{ @@ -1230,6 +1201,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { rpc.On("Dial", mock.Anything).Return(nil) rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) + node.declareUnreachable() tests.AssertEventually(t, func() bool { return node.State() == nodeStateInvalidChainID @@ -1237,7 +1209,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { }) t.Run("on syncing status check failure, keeps trying", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newAliveNode(t, testNodeOpts{ @@ -1258,7 +1230,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { }) t.Run("on syncing, transitions to syncing state", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() node := newAliveNode(t, testNodeOpts{ rpc: rpc, @@ -1280,7 +1252,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { }) t.Run("on successful verification becomes alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() node := newAliveNode(t, testNodeOpts{ rpc: rpc, @@ -1289,10 +1261,8 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() - rpc.On("Dial", mock.Anything).Return(nil) rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) rpc.On("IsSyncing", mock.Anything).Return(false, nil) - setupRPCForAliveLoop(t, rpc) node.declareUnreachable() @@ -1302,7 +1272,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { }) t.Run("on successful verification without isSyncing becomes alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() node := newAliveNode(t, testNodeOpts{ rpc: rpc, @@ -1326,8 +1296,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { t.Parallel() newDialedNode := func(t *testing.T, opts testNodeOpts) testNode { node := newTestNode(t, opts) - opts.rpc.On("Close").Return(nil).Once() - opts.rpc.On("DisconnectAll") + opts.rpc.On("Close").Return(nil) node.setState(nodeStateDialed) return node @@ -1341,7 +1310,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { }) t.Run("on invalid dial becomes unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() node := newDialedNode(t, testNodeOpts{ rpc: rpc, @@ -1350,6 +1319,8 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")) + rpc.On("Close") + node.declareInvalidChainID() tests.AssertEventually(t, func() bool { return node.State() == nodeStateUnreachable @@ -1357,7 +1328,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { }) t.Run("on failed chainID call becomes unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newDialedNode(t, testNodeOpts{ @@ -1371,6 +1342,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { // once for chainID and maybe another one for unreachable rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() + node.declareInvalidChainID() tests.AssertLogEventually(t, observedLogs, "Failed to verify chain ID for node") tests.AssertEventually(t, func() bool { @@ -1379,7 +1351,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { }) t.Run("on chainID mismatch keeps trying", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) rpcChainID := types.NewIDFromInt(11) lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) @@ -1392,6 +1364,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) + node.declareInvalidChainID() tests.AssertLogCountEventually(t, observedLogs, "Failed to verify RPC node; remote endpoint returned the wrong chain ID", 2) tests.AssertEventually(t, func() bool { @@ -1400,7 +1373,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { }) t.Run("on successful verification without isSyncing becomes alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) rpcChainID := types.NewIDFromInt(11) node := newDialedNode(t, testNodeOpts{ @@ -1409,12 +1382,10 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() - rpc.On("Dial", mock.Anything).Return(nil).Once() + setupRPCForAliveLoop(t, rpc) rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil).Once() rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() - setupRPCForAliveLoop(t, rpc) - node.declareInvalidChainID() tests.AssertEventually(t, func() bool { return node.State() == nodeStateAlive @@ -1422,7 +1393,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { }) t.Run("on successful verification becomes alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) rpcChainID := types.NewIDFromInt(11) node := newDialedNode(t, testNodeOpts{ @@ -1432,7 +1403,6 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() - rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil).Once() rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() rpc.On("IsSyncing", mock.Anything).Return(false, nil).Once() @@ -1451,13 +1421,13 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { newNode := func(t *testing.T, opts testNodeOpts) testNode { node := newTestNode(t, opts) - opts.rpc.On("Close").Return(nil).Once() + opts.rpc.On("Close").Return(nil) return node } t.Run("if fails on initial dial, becomes unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newNode(t, testNodeOpts{ @@ -1468,8 +1438,6 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")) - // disconnects all on transfer to unreachable - rpc.On("DisconnectAll") err := node.Start(tests.Context(t)) assert.NoError(t, err) tests.AssertLogEventually(t, observedLogs, "Dial failed: Node is unreachable") @@ -1479,7 +1447,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { }) t.Run("if chainID verification fails, becomes unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newNode(t, testNodeOpts{ @@ -1493,8 +1461,6 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { rpc.On("ChainID", mock.Anything).Run(func(_ mock.Arguments) { assert.Equal(t, nodeStateDialed, node.State()) }).Return(nodeChainID, errors.New("failed to get chain id")) - // disconnects all on transfer to unreachable - rpc.On("DisconnectAll") err := node.Start(tests.Context(t)) assert.NoError(t, err) tests.AssertLogEventually(t, observedLogs, "Failed to verify chain ID for node") @@ -1504,7 +1470,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { }) t.Run("on chain ID mismatch transitions to invalidChainID", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) rpcChainID := types.NewIDFromInt(11) node := newNode(t, testNodeOpts{ @@ -1514,9 +1480,8 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(nil) + rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) - // disconnects all on transfer to unreachable - rpc.On("DisconnectAll") err := node.Start(tests.Context(t)) assert.NoError(t, err) tests.AssertEventually(t, func() bool { @@ -1525,7 +1490,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { }) t.Run("if syncing verification fails, becomes unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newNode(t, testNodeOpts{ @@ -1537,13 +1502,11 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(nil).Once() + rpc.On("ChainID", mock.Anything).Run(func(_ mock.Arguments) { assert.Equal(t, nodeStateDialed, node.State()) }).Return(nodeChainID, nil).Once() rpc.On("IsSyncing", mock.Anything).Return(false, errors.New("failed to check syncing status")) - // disconnects all on transfer to unreachable - rpc.On("DisconnectAll") - // fail to redial to stay in unreachable state rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")) err := node.Start(tests.Context(t)) assert.NoError(t, err) @@ -1554,7 +1517,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { }) t.Run("on isSyncing transitions to syncing", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) node := newNode(t, testNodeOpts{ rpc: rpc, @@ -1564,10 +1527,9 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(nil) + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) rpc.On("IsSyncing", mock.Anything).Return(true, nil) - // disconnects all on transfer to unreachable - rpc.On("DisconnectAll") err := node.Start(tests.Context(t)) assert.NoError(t, err) tests.AssertEventually(t, func() bool { @@ -1576,7 +1538,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { }) t.Run("on successful verification becomes alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() node := newNode(t, testNodeOpts{ rpc: rpc, @@ -1585,10 +1547,8 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() - rpc.On("Dial", mock.Anything).Return(nil) rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) rpc.On("IsSyncing", mock.Anything).Return(false, nil) - setupRPCForAliveLoop(t, rpc) err := node.Start(tests.Context(t)) @@ -1599,7 +1559,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { }) t.Run("on successful verification without isSyncing becomes alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() node := newNode(t, testNodeOpts{ rpc: rpc, @@ -1607,9 +1567,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() - rpc.On("Dial", mock.Anything).Return(nil) rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) - setupRPCForAliveLoop(t, rpc) err := node.Start(tests.Context(t)) @@ -1765,8 +1723,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { newDialedNode := func(t *testing.T, opts testNodeOpts) testNode { opts.config.nodeIsSyncingEnabled = true node := newTestNode(t, opts) - opts.rpc.On("Close").Return(nil).Once() - opts.rpc.On("DisconnectAll") + opts.rpc.On("Close").Return(nil) node.setState(nodeStateDialed) return node @@ -1780,7 +1737,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { }) t.Run("on invalid dial becomes unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() node := newDialedNode(t, testNodeOpts{ rpc: rpc, @@ -1789,6 +1746,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")) + node.declareSyncing() tests.AssertEventually(t, func() bool { return node.State() == nodeStateUnreachable @@ -1796,7 +1754,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { }) t.Run("on failed chainID call becomes unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newDialedNode(t, testNodeOpts{ @@ -1807,6 +1765,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("ChainID", mock.Anything).Return(nodeChainID, errors.New("failed to get chain id")) + // once for syncing and maybe another one for unreachable rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() @@ -1818,7 +1777,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { }) t.Run("on chainID mismatch transitions to invalidChainID", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) rpcChainID := types.NewIDFromInt(11) lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) @@ -1830,6 +1789,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(nil).Twice() + rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) node.declareSyncing() tests.AssertLogCountEventually(t, observedLogs, "Failed to verify RPC node; remote endpoint returned the wrong chain ID", 2) @@ -1839,7 +1799,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { }) t.Run("on failed Syncing check - becomes unreachable", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newDialedNode(t, testNodeOpts{ @@ -1855,6 +1815,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { rpc.On("IsSyncing", mock.Anything).Return(false, errors.New("failed to check if syncing")).Once() rpc.On("Dial", mock.Anything).Return(nil).Once() rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() + node.declareSyncing() tests.AssertLogEventually(t, observedLogs, "Unexpected error while verifying RPC node synchronization status") tests.AssertEventually(t, func() bool { @@ -1863,7 +1824,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { }) t.Run("on IsSyncing - keeps trying", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newDialedNode(t, testNodeOpts{ @@ -1876,6 +1837,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() rpc.On("IsSyncing", mock.Anything).Return(true, nil) rpc.On("Dial", mock.Anything).Return(nil).Once() + node.declareSyncing() tests.AssertLogCountEventually(t, observedLogs, "Verification failed: Node is syncing", 2) tests.AssertEventually(t, func() bool { @@ -1884,7 +1846,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { }) t.Run("on successful verification becomes alive", func(t *testing.T) { t.Parallel() - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) nodeChainID := types.RandomID() node := newDialedNode(t, testNodeOpts{ rpc: rpc, @@ -1893,6 +1855,7 @@ func TestUnit_NodeLifecycle_SyncingLoop(t *testing.T) { defer func() { assert.NoError(t, node.close()) }() rpc.On("Dial", mock.Anything).Return(nil).Once() + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() rpc.On("IsSyncing", mock.Anything).Return(true, nil).Once() rpc.On("IsSyncing", mock.Anything).Return(false, nil).Once() @@ -1980,7 +1943,7 @@ func TestNode_State(t *testing.T) { } for _, tc := range testCases { t.Run(tc.Name, func(t *testing.T) { - rpc := newMockNodeClient[types.ID, Head](t) + rpc := newMockRPCClient[types.ID, Head](t) rpc.On("GetInterceptedChainInfo").Return(tc.NodeChainInfo, tc.PoolChainInfo).Once() node := newTestNode(t, testNodeOpts{ config: testNodeConfig{ diff --git a/common/client/node_selector.go b/common/client/node_selector.go index 4aae3734eea..372b521bb1c 100644 --- a/common/client/node_selector.go +++ b/common/client/node_selector.go @@ -15,30 +15,28 @@ const ( type NodeSelector[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], + RPC any, ] interface { // Select returns a Node, or nil if none can be selected. // Implementation must be thread-safe. - Select() Node[CHAIN_ID, HEAD, RPC] + Select() Node[CHAIN_ID, RPC] // Name returns the strategy name, e.g. "HighestHead" or "RoundRobin" Name() string } func newNodeSelector[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -](selectionMode string, nodes []Node[CHAIN_ID, HEAD, RPC]) NodeSelector[CHAIN_ID, HEAD, RPC] { + RPC any, +](selectionMode string, nodes []Node[CHAIN_ID, RPC]) NodeSelector[CHAIN_ID, RPC] { switch selectionMode { case NodeSelectionModeHighestHead: - return NewHighestHeadNodeSelector[CHAIN_ID, HEAD, RPC](nodes) + return NewHighestHeadNodeSelector[CHAIN_ID, RPC](nodes) case NodeSelectionModeRoundRobin: - return NewRoundRobinSelector[CHAIN_ID, HEAD, RPC](nodes) + return NewRoundRobinSelector[CHAIN_ID, RPC](nodes) case NodeSelectionModeTotalDifficulty: - return NewTotalDifficultyNodeSelector[CHAIN_ID, HEAD, RPC](nodes) + return NewTotalDifficultyNodeSelector[CHAIN_ID, RPC](nodes) case NodeSelectionModePriorityLevel: - return NewPriorityLevelNodeSelector[CHAIN_ID, HEAD, RPC](nodes) + return NewPriorityLevelNodeSelector[CHAIN_ID, RPC](nodes) default: panic(fmt.Sprintf("unsupported NodeSelectionMode: %s", selectionMode)) } diff --git a/common/client/node_selector_highest_head.go b/common/client/node_selector_highest_head.go index 25a931fc01b..454584a77e1 100644 --- a/common/client/node_selector_highest_head.go +++ b/common/client/node_selector_highest_head.go @@ -8,21 +8,19 @@ import ( type highestHeadNodeSelector[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -] []Node[CHAIN_ID, HEAD, RPC] + RPC any, +] []Node[CHAIN_ID, RPC] func NewHighestHeadNodeSelector[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -](nodes []Node[CHAIN_ID, HEAD, RPC]) NodeSelector[CHAIN_ID, HEAD, RPC] { - return highestHeadNodeSelector[CHAIN_ID, HEAD, RPC](nodes) + RPC any, +](nodes []Node[CHAIN_ID, RPC]) NodeSelector[CHAIN_ID, RPC] { + return highestHeadNodeSelector[CHAIN_ID, RPC](nodes) } -func (s highestHeadNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, RPC] { +func (s highestHeadNodeSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] { var highestHeadNumber int64 = math.MinInt64 - var highestHeadNodes []Node[CHAIN_ID, HEAD, RPC] + var highestHeadNodes []Node[CHAIN_ID, RPC] for _, n := range s { state, currentChainInfo := n.StateAndLatest() currentHeadNumber := currentChainInfo.BlockNumber @@ -37,6 +35,6 @@ func (s highestHeadNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HE return firstOrHighestPriority(highestHeadNodes) } -func (s highestHeadNodeSelector[CHAIN_ID, HEAD, RPC]) Name() string { +func (s highestHeadNodeSelector[CHAIN_ID, RPC]) Name() string { return NodeSelectionModeHighestHead } diff --git a/common/client/node_selector_highest_head_test.go b/common/client/node_selector_highest_head_test.go index e245924589c..ebee3f403e2 100644 --- a/common/client/node_selector_highest_head_test.go +++ b/common/client/node_selector_highest_head_test.go @@ -9,19 +9,19 @@ import ( ) func TestHighestHeadNodeSelectorName(t *testing.T) { - selector := newNodeSelector[types.ID, Head, NodeClient[types.ID, Head]](NodeSelectionModeHighestHead, nil) + selector := newNodeSelector[types.ID, RPCClient[types.ID, Head]](NodeSelectionModeHighestHead, nil) assert.Equal(t, selector.Name(), NodeSelectionModeHighestHead) } func TestHighestHeadNodeSelector(t *testing.T) { t.Parallel() - type nodeClient NodeClient[types.ID, Head] + type nodeClient RPCClient[types.ID, Head] - var nodes []Node[types.ID, Head, nodeClient] + var nodes []Node[types.ID, nodeClient] for i := 0; i < 3; i++ { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) if i == 0 { // first node is out of sync node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: int64(-1)}) @@ -36,11 +36,11 @@ func TestHighestHeadNodeSelector(t *testing.T) { nodes = append(nodes, node) } - selector := newNodeSelector[types.ID, Head, nodeClient](NodeSelectionModeHighestHead, nodes) + selector := newNodeSelector[types.ID, nodeClient](NodeSelectionModeHighestHead, nodes) assert.Same(t, nodes[2], selector.Select()) t.Run("stick to the same node", func(t *testing.T) { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) // fourth node is alive, LatestReceivedBlockNumber = 2 (same as 3rd) node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)}) node.On("Order").Return(int32(1)) @@ -51,7 +51,7 @@ func TestHighestHeadNodeSelector(t *testing.T) { }) t.Run("another best node", func(t *testing.T) { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) // fifth node is alive, LatestReceivedBlockNumber = 3 (better than 3rd and 4th) node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node.On("Order").Return(int32(1)) @@ -62,13 +62,13 @@ func TestHighestHeadNodeSelector(t *testing.T) { }) t.Run("nodes never update latest block number", func(t *testing.T) { - node1 := newMockNode[types.ID, Head, nodeClient](t) + node1 := newMockNode[types.ID, nodeClient](t) node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(-1)}) node1.On("Order").Return(int32(1)) - node2 := newMockNode[types.ID, Head, nodeClient](t) + node2 := newMockNode[types.ID, nodeClient](t) node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(-1)}) node2.On("Order").Return(int32(1)) - selector := newNodeSelector(NodeSelectionModeHighestHead, []Node[types.ID, Head, nodeClient]{node1, node2}) + selector := newNodeSelector(NodeSelectionModeHighestHead, []Node[types.ID, nodeClient]{node1, node2}) assert.Same(t, node1, selector.Select()) }) } @@ -76,11 +76,11 @@ func TestHighestHeadNodeSelector(t *testing.T) { func TestHighestHeadNodeSelector_None(t *testing.T) { t.Parallel() - type nodeClient NodeClient[types.ID, Head] - var nodes []Node[types.ID, Head, nodeClient] + type nodeClient RPCClient[types.ID, Head] + var nodes []Node[types.ID, nodeClient] for i := 0; i < 3; i++ { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) if i == 0 { // first node is out of sync node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: int64(-1)}) @@ -98,12 +98,12 @@ func TestHighestHeadNodeSelector_None(t *testing.T) { func TestHighestHeadNodeSelectorWithOrder(t *testing.T) { t.Parallel() - type nodeClient NodeClient[types.ID, Head] - var nodes []Node[types.ID, Head, nodeClient] + type nodeClient RPCClient[types.ID, Head] + var nodes []Node[types.ID, nodeClient] t.Run("same head and order", func(t *testing.T) { for i := 0; i < 3; i++ { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)}) node.On("Order").Return(int32(2)) nodes = append(nodes, node) @@ -114,61 +114,61 @@ func TestHighestHeadNodeSelectorWithOrder(t *testing.T) { }) t.Run("same head but different order", func(t *testing.T) { - node1 := newMockNode[types.ID, Head, nodeClient](t) + node1 := newMockNode[types.ID, nodeClient](t) node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node1.On("Order").Return(int32(3)) - node2 := newMockNode[types.ID, Head, nodeClient](t) + node2 := newMockNode[types.ID, nodeClient](t) node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node2.On("Order").Return(int32(1)) - node3 := newMockNode[types.ID, Head, nodeClient](t) + node3 := newMockNode[types.ID, nodeClient](t) node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node3.On("Order").Return(int32(2)) - nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} + nodes := []Node[types.ID, nodeClient]{node1, node2, node3} selector := newNodeSelector(NodeSelectionModeHighestHead, nodes) //Should select the second node as it has the highest priority assert.Same(t, nodes[1], selector.Select()) }) t.Run("different head but same order", func(t *testing.T) { - node1 := newMockNode[types.ID, Head, nodeClient](t) + node1 := newMockNode[types.ID, nodeClient](t) node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(1)}) node1.On("Order").Maybe().Return(int32(3)) - node2 := newMockNode[types.ID, Head, nodeClient](t) + node2 := newMockNode[types.ID, nodeClient](t) node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(2)}) node2.On("Order").Maybe().Return(int32(3)) - node3 := newMockNode[types.ID, Head, nodeClient](t) + node3 := newMockNode[types.ID, nodeClient](t) node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(3)}) node3.On("Order").Return(int32(3)) - nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} + nodes := []Node[types.ID, nodeClient]{node1, node2, node3} selector := newNodeSelector(NodeSelectionModeHighestHead, nodes) //Should select the third node as it has the highest head assert.Same(t, nodes[2], selector.Select()) }) t.Run("different head and different order", func(t *testing.T) { - node1 := newMockNode[types.ID, Head, nodeClient](t) + node1 := newMockNode[types.ID, nodeClient](t) node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(10)}) node1.On("Order").Maybe().Return(int32(3)) - node2 := newMockNode[types.ID, Head, nodeClient](t) + node2 := newMockNode[types.ID, nodeClient](t) node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(11)}) node2.On("Order").Maybe().Return(int32(4)) - node3 := newMockNode[types.ID, Head, nodeClient](t) + node3 := newMockNode[types.ID, nodeClient](t) node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(11)}) node3.On("Order").Maybe().Return(int32(3)) - node4 := newMockNode[types.ID, Head, nodeClient](t) + node4 := newMockNode[types.ID, nodeClient](t) node4.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: int64(10)}) node4.On("Order").Maybe().Return(int32(1)) - nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3, node4} + nodes := []Node[types.ID, nodeClient]{node1, node2, node3, node4} selector := newNodeSelector(NodeSelectionModeHighestHead, nodes) //Should select the third node as it has the highest head and will win the priority tie-breaker assert.Same(t, nodes[2], selector.Select()) diff --git a/common/client/node_selector_priority_level.go b/common/client/node_selector_priority_level.go index 45cc62de077..6d6784fb216 100644 --- a/common/client/node_selector_priority_level.go +++ b/common/client/node_selector_priority_level.go @@ -10,34 +10,31 @@ import ( type priorityLevelNodeSelector[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], + RPC any, ] struct { - nodes []Node[CHAIN_ID, HEAD, RPC] + nodes []Node[CHAIN_ID, RPC] roundRobinCount []atomic.Uint32 } type nodeWithPriority[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], + RPC any, ] struct { - node Node[CHAIN_ID, HEAD, RPC] + node Node[CHAIN_ID, RPC] priority int32 } func NewPriorityLevelNodeSelector[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -](nodes []Node[CHAIN_ID, HEAD, RPC]) NodeSelector[CHAIN_ID, HEAD, RPC] { - return &priorityLevelNodeSelector[CHAIN_ID, HEAD, RPC]{ + RPC any, +](nodes []Node[CHAIN_ID, RPC]) NodeSelector[CHAIN_ID, RPC] { + return &priorityLevelNodeSelector[CHAIN_ID, RPC]{ nodes: nodes, roundRobinCount: make([]atomic.Uint32, nrOfPriorityTiers(nodes)), } } -func (s priorityLevelNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, RPC] { +func (s priorityLevelNodeSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] { nodes := s.getHighestPriorityAliveTier() if len(nodes) == 0 { @@ -52,17 +49,17 @@ func (s priorityLevelNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, return nodes[idx].node } -func (s priorityLevelNodeSelector[CHAIN_ID, HEAD, RPC]) Name() string { +func (s priorityLevelNodeSelector[CHAIN_ID, RPC]) Name() string { return NodeSelectionModePriorityLevel } // getHighestPriorityAliveTier filters nodes that are not in state nodeStateAlive and // returns only the highest tier of alive nodes -func (s priorityLevelNodeSelector[CHAIN_ID, HEAD, RPC]) getHighestPriorityAliveTier() []nodeWithPriority[CHAIN_ID, HEAD, RPC] { - var nodes []nodeWithPriority[CHAIN_ID, HEAD, RPC] +func (s priorityLevelNodeSelector[CHAIN_ID, RPC]) getHighestPriorityAliveTier() []nodeWithPriority[CHAIN_ID, RPC] { + var nodes []nodeWithPriority[CHAIN_ID, RPC] for _, n := range s.nodes { if n.State() == nodeStateAlive { - nodes = append(nodes, nodeWithPriority[CHAIN_ID, HEAD, RPC]{n, n.Order()}) + nodes = append(nodes, nodeWithPriority[CHAIN_ID, RPC]{n, n.Order()}) } } @@ -76,14 +73,13 @@ func (s priorityLevelNodeSelector[CHAIN_ID, HEAD, RPC]) getHighestPriorityAliveT // removeLowerTiers take a slice of nodeWithPriority[CHAIN_ID, BLOCK_HASH, HEAD, RPC] and keeps only the highest tier func removeLowerTiers[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -](nodes []nodeWithPriority[CHAIN_ID, HEAD, RPC]) []nodeWithPriority[CHAIN_ID, HEAD, RPC] { + RPC any, +](nodes []nodeWithPriority[CHAIN_ID, RPC]) []nodeWithPriority[CHAIN_ID, RPC] { sort.SliceStable(nodes, func(i, j int) bool { return nodes[i].priority > nodes[j].priority }) - var nodes2 []nodeWithPriority[CHAIN_ID, HEAD, RPC] + var nodes2 []nodeWithPriority[CHAIN_ID, RPC] currentPriority := nodes[len(nodes)-1].priority for _, n := range nodes { @@ -98,9 +94,8 @@ func removeLowerTiers[ // nrOfPriorityTiers calculates the total number of priority tiers func nrOfPriorityTiers[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -](nodes []Node[CHAIN_ID, HEAD, RPC]) int32 { + RPC any, +](nodes []Node[CHAIN_ID, RPC]) int32 { highestPriority := int32(0) for _, n := range nodes { priority := n.Order() @@ -114,11 +109,10 @@ func nrOfPriorityTiers[ // firstOrHighestPriority takes a list of nodes and returns the first one with the highest priority func firstOrHighestPriority[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -](nodes []Node[CHAIN_ID, HEAD, RPC]) Node[CHAIN_ID, HEAD, RPC] { + RPC any, +](nodes []Node[CHAIN_ID, RPC]) Node[CHAIN_ID, RPC] { hp := int32(math.MaxInt32) - var node Node[CHAIN_ID, HEAD, RPC] + var node Node[CHAIN_ID, RPC] for _, n := range nodes { if n.Order() < hp { hp = n.Order() diff --git a/common/client/node_selector_priority_level_test.go b/common/client/node_selector_priority_level_test.go index 15a7a7ac60b..67aac97be1b 100644 --- a/common/client/node_selector_priority_level_test.go +++ b/common/client/node_selector_priority_level_test.go @@ -9,14 +9,14 @@ import ( ) func TestPriorityLevelNodeSelectorName(t *testing.T) { - selector := newNodeSelector[types.ID, Head, NodeClient[types.ID, Head]](NodeSelectionModePriorityLevel, nil) + selector := newNodeSelector[types.ID, RPCClient[types.ID, Head]](NodeSelectionModePriorityLevel, nil) assert.Equal(t, selector.Name(), NodeSelectionModePriorityLevel) } func TestPriorityLevelNodeSelector(t *testing.T) { t.Parallel() - type nodeClient NodeClient[types.ID, Head] + type nodeClient RPCClient[types.ID, Head] type testNode struct { order int32 state nodeState @@ -66,9 +66,9 @@ func TestPriorityLevelNodeSelector(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - var nodes []Node[types.ID, Head, nodeClient] + var nodes []Node[types.ID, nodeClient] for _, tn := range tc.nodes { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) node.On("State").Return(tn.state) node.On("Order").Return(tn.order) nodes = append(nodes, node) diff --git a/common/client/node_selector_round_robin.go b/common/client/node_selector_round_robin.go index 5cdad7f52ee..18cea03ebd5 100644 --- a/common/client/node_selector_round_robin.go +++ b/common/client/node_selector_round_robin.go @@ -8,25 +8,23 @@ import ( type roundRobinSelector[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], + RPC any, ] struct { - nodes []Node[CHAIN_ID, HEAD, RPC] + nodes []Node[CHAIN_ID, RPC] roundRobinCount atomic.Uint32 } func NewRoundRobinSelector[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -](nodes []Node[CHAIN_ID, HEAD, RPC]) NodeSelector[CHAIN_ID, HEAD, RPC] { - return &roundRobinSelector[CHAIN_ID, HEAD, RPC]{ + RPC any, +](nodes []Node[CHAIN_ID, RPC]) NodeSelector[CHAIN_ID, RPC] { + return &roundRobinSelector[CHAIN_ID, RPC]{ nodes: nodes, } } -func (s *roundRobinSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, RPC] { - var liveNodes []Node[CHAIN_ID, HEAD, RPC] +func (s *roundRobinSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] { + var liveNodes []Node[CHAIN_ID, RPC] for _, n := range s.nodes { if n.State() == nodeStateAlive { liveNodes = append(liveNodes, n) @@ -45,6 +43,6 @@ func (s *roundRobinSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, return liveNodes[idx] } -func (s *roundRobinSelector[CHAIN_ID, HEAD, RPC]) Name() string { +func (s *roundRobinSelector[CHAIN_ID, RPC]) Name() string { return NodeSelectionModeRoundRobin } diff --git a/common/client/node_selector_round_robin_test.go b/common/client/node_selector_round_robin_test.go index e5078d858f1..189b58da9ea 100644 --- a/common/client/node_selector_round_robin_test.go +++ b/common/client/node_selector_round_robin_test.go @@ -9,18 +9,18 @@ import ( ) func TestRoundRobinNodeSelectorName(t *testing.T) { - selector := newNodeSelector[types.ID, Head, NodeClient[types.ID, Head]](NodeSelectionModeRoundRobin, nil) + selector := newNodeSelector[types.ID, RPCClient[types.ID, Head]](NodeSelectionModeRoundRobin, nil) assert.Equal(t, selector.Name(), NodeSelectionModeRoundRobin) } func TestRoundRobinNodeSelector(t *testing.T) { t.Parallel() - type nodeClient NodeClient[types.ID, Head] - var nodes []Node[types.ID, Head, nodeClient] + type nodeClient RPCClient[types.ID, Head] + var nodes []Node[types.ID, nodeClient] for i := 0; i < 3; i++ { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) if i == 0 { // first node is out of sync node.On("State").Return(nodeStateOutOfSync) @@ -41,11 +41,11 @@ func TestRoundRobinNodeSelector(t *testing.T) { func TestRoundRobinNodeSelector_None(t *testing.T) { t.Parallel() - type nodeClient NodeClient[types.ID, Head] - var nodes []Node[types.ID, Head, nodeClient] + type nodeClient RPCClient[types.ID, Head] + var nodes []Node[types.ID, nodeClient] for i := 0; i < 3; i++ { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) if i == 0 { // first node is out of sync node.On("State").Return(nodeStateOutOfSync) diff --git a/common/client/node_selector_test.go b/common/client/node_selector_test.go index 226cb67168d..f652bfc50ad 100644 --- a/common/client/node_selector_test.go +++ b/common/client/node_selector_test.go @@ -12,7 +12,7 @@ func TestNodeSelector(t *testing.T) { // rest of the tests are located in specific node selectors tests t.Run("panics on unknown type", func(t *testing.T) { assert.Panics(t, func() { - _ = newNodeSelector[types.ID, Head, NodeClient[types.ID, Head]]("unknown", nil) + _ = newNodeSelector[types.ID, RPCClient[types.ID, Head]]("unknown", nil) }) }) } diff --git a/common/client/node_selector_total_difficulty.go b/common/client/node_selector_total_difficulty.go index 6b45e75528b..7defcd741ca 100644 --- a/common/client/node_selector_total_difficulty.go +++ b/common/client/node_selector_total_difficulty.go @@ -8,23 +8,21 @@ import ( type totalDifficultyNodeSelector[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -] []Node[CHAIN_ID, HEAD, RPC] + RPC any, +] []Node[CHAIN_ID, RPC] func NewTotalDifficultyNodeSelector[ CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -](nodes []Node[CHAIN_ID, HEAD, RPC]) NodeSelector[CHAIN_ID, HEAD, RPC] { - return totalDifficultyNodeSelector[CHAIN_ID, HEAD, RPC](nodes) + RPC any, +](nodes []Node[CHAIN_ID, RPC]) NodeSelector[CHAIN_ID, RPC] { + return totalDifficultyNodeSelector[CHAIN_ID, RPC](nodes) } -func (s totalDifficultyNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, RPC] { +func (s totalDifficultyNodeSelector[CHAIN_ID, RPC]) Select() Node[CHAIN_ID, RPC] { // NodeNoNewHeadsThreshold may not be enabled, in this case all nodes have td == nil var highestTD *big.Int - var nodes []Node[CHAIN_ID, HEAD, RPC] - var aliveNodes []Node[CHAIN_ID, HEAD, RPC] + var nodes []Node[CHAIN_ID, RPC] + var aliveNodes []Node[CHAIN_ID, RPC] for _, n := range s { state, currentChainInfo := n.StateAndLatest() @@ -50,6 +48,6 @@ func (s totalDifficultyNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID return firstOrHighestPriority(nodes) } -func (s totalDifficultyNodeSelector[CHAIN_ID, HEAD, RPC]) Name() string { +func (s totalDifficultyNodeSelector[CHAIN_ID, RPC]) Name() string { return NodeSelectionModeTotalDifficulty } diff --git a/common/client/node_selector_total_difficulty_test.go b/common/client/node_selector_total_difficulty_test.go index 0bc214918d7..ed45cfdf9ac 100644 --- a/common/client/node_selector_total_difficulty_test.go +++ b/common/client/node_selector_total_difficulty_test.go @@ -10,18 +10,18 @@ import ( ) func TestTotalDifficultyNodeSelectorName(t *testing.T) { - selector := newNodeSelector[types.ID, Head, NodeClient[types.ID, Head]](NodeSelectionModeTotalDifficulty, nil) + selector := newNodeSelector[types.ID, RPCClient[types.ID, Head]](NodeSelectionModeTotalDifficulty, nil) assert.Equal(t, selector.Name(), NodeSelectionModeTotalDifficulty) } func TestTotalDifficultyNodeSelector(t *testing.T) { t.Parallel() - type nodeClient NodeClient[types.ID, Head] - var nodes []Node[types.ID, Head, nodeClient] + type nodeClient RPCClient[types.ID, Head] + var nodes []Node[types.ID, nodeClient] for i := 0; i < 3; i++ { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) if i == 0 { // first node is out of sync node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: -1}) @@ -40,7 +40,7 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { assert.Same(t, nodes[2], selector.Select()) t.Run("stick to the same node", func(t *testing.T) { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) // fourth node is alive (same as 3rd) node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 2, TotalDifficulty: big.NewInt(8)}) node.On("Order").Maybe().Return(int32(1)) @@ -51,7 +51,7 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { }) t.Run("another best node", func(t *testing.T) { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) // fifth node is alive (better than 3rd and 4th) node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(11)}) node.On("Order").Maybe().Return(int32(1)) @@ -62,13 +62,13 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { }) t.Run("nodes never update latest block number", func(t *testing.T) { - node1 := newMockNode[types.ID, Head, nodeClient](t) + node1 := newMockNode[types.ID, nodeClient](t) node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: -1, TotalDifficulty: nil}) node1.On("Order").Maybe().Return(int32(1)) - node2 := newMockNode[types.ID, Head, nodeClient](t) + node2 := newMockNode[types.ID, nodeClient](t) node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: -1, TotalDifficulty: nil}) node2.On("Order").Maybe().Return(int32(1)) - nodes := []Node[types.ID, Head, nodeClient]{node1, node2} + nodes := []Node[types.ID, nodeClient]{node1, node2} selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes) assert.Same(t, node1, selector.Select()) @@ -78,11 +78,11 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { func TestTotalDifficultyNodeSelector_None(t *testing.T) { t.Parallel() - type nodeClient NodeClient[types.ID, Head] - var nodes []Node[types.ID, Head, nodeClient] + type nodeClient RPCClient[types.ID, Head] + var nodes []Node[types.ID, nodeClient] for i := 0; i < 3; i++ { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) if i == 0 { // first node is out of sync node.On("StateAndLatest").Return(nodeStateOutOfSync, ChainInfo{BlockNumber: -1, TotalDifficulty: nil}) @@ -100,12 +100,12 @@ func TestTotalDifficultyNodeSelector_None(t *testing.T) { func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Parallel() - type nodeClient NodeClient[types.ID, Head] - var nodes []Node[types.ID, Head, nodeClient] + type nodeClient RPCClient[types.ID, Head] + var nodes []Node[types.ID, nodeClient] t.Run("same td and order", func(t *testing.T) { for i := 0; i < 3; i++ { - node := newMockNode[types.ID, Head, nodeClient](t) + node := newMockNode[types.ID, nodeClient](t) node.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(10)}) node.On("Order").Return(int32(2)) nodes = append(nodes, node) @@ -116,61 +116,61 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { }) t.Run("same td but different order", func(t *testing.T) { - node1 := newMockNode[types.ID, Head, nodeClient](t) + node1 := newMockNode[types.ID, nodeClient](t) node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)}) node1.On("Order").Return(int32(3)) - node2 := newMockNode[types.ID, Head, nodeClient](t) + node2 := newMockNode[types.ID, nodeClient](t) node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)}) node2.On("Order").Return(int32(1)) - node3 := newMockNode[types.ID, Head, nodeClient](t) + node3 := newMockNode[types.ID, nodeClient](t) node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 3, TotalDifficulty: big.NewInt(10)}) node3.On("Order").Return(int32(2)) - nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} + nodes := []Node[types.ID, nodeClient]{node1, node2, node3} selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes) //Should select the second node as it has the highest priority assert.Same(t, nodes[1], selector.Select()) }) t.Run("different td but same order", func(t *testing.T) { - node1 := newMockNode[types.ID, Head, nodeClient](t) + node1 := newMockNode[types.ID, nodeClient](t) node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(10)}) node1.On("Order").Maybe().Return(int32(3)) - node2 := newMockNode[types.ID, Head, nodeClient](t) + node2 := newMockNode[types.ID, nodeClient](t) node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(11)}) node2.On("Order").Maybe().Return(int32(3)) - node3 := newMockNode[types.ID, Head, nodeClient](t) + node3 := newMockNode[types.ID, nodeClient](t) node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(12)}) node3.On("Order").Return(int32(3)) - nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} + nodes := []Node[types.ID, nodeClient]{node1, node2, node3} selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes) //Should select the third node as it has the highest td assert.Same(t, nodes[2], selector.Select()) }) t.Run("different head and different order", func(t *testing.T) { - node1 := newMockNode[types.ID, Head, nodeClient](t) + node1 := newMockNode[types.ID, nodeClient](t) node1.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(100)}) node1.On("Order").Maybe().Return(int32(4)) - node2 := newMockNode[types.ID, Head, nodeClient](t) + node2 := newMockNode[types.ID, nodeClient](t) node2.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(110)}) node2.On("Order").Maybe().Return(int32(5)) - node3 := newMockNode[types.ID, Head, nodeClient](t) + node3 := newMockNode[types.ID, nodeClient](t) node3.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(110)}) node3.On("Order").Maybe().Return(int32(1)) - node4 := newMockNode[types.ID, Head, nodeClient](t) + node4 := newMockNode[types.ID, nodeClient](t) node4.On("StateAndLatest").Return(nodeStateAlive, ChainInfo{BlockNumber: 1, TotalDifficulty: big.NewInt(105)}) node4.On("Order").Maybe().Return(int32(2)) - nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3, node4} + nodes := []Node[types.ID, nodeClient]{node1, node2, node3, node4} selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes) //Should select the third node as it has the highest td and will win the priority tie-breaker assert.Same(t, nodes[2], selector.Select()) diff --git a/common/client/node_test.go b/common/client/node_test.go index 539964691c0..6d98c2d9ea4 100644 --- a/common/client/node_test.go +++ b/common/client/node_test.go @@ -60,7 +60,7 @@ func (n testNodeConfig) DeathDeclarationDelay() time.Duration { } type testNode struct { - *node[types.ID, Head, NodeClient[types.ID, Head]] + *node[types.ID, Head, RPCClient[types.ID, Head]] } type testNodeOpts struct { @@ -73,7 +73,7 @@ type testNodeOpts struct { id int chainID types.ID nodeOrder int32 - rpc *mockNodeClient[types.ID, Head] + rpc *mockRPCClient[types.ID, Head] chainFamily string } @@ -98,10 +98,10 @@ func newTestNode(t *testing.T, opts testNodeOpts) testNode { opts.id = 42 } - nodeI := NewNode[types.ID, Head, NodeClient[types.ID, Head]](opts.config, opts.chainConfig, opts.lggr, + nodeI := NewNode[types.ID, Head, RPCClient[types.ID, Head]](opts.config, opts.chainConfig, opts.lggr, opts.wsuri, opts.httpuri, opts.name, opts.id, opts.chainID, opts.nodeOrder, opts.rpc, opts.chainFamily) return testNode{ - nodeI.(*node[types.ID, Head, NodeClient[types.ID, Head]]), + nodeI.(*node[types.ID, Head, RPCClient[types.ID, Head]]), } } diff --git a/common/client/send_only_node.go b/common/client/send_only_node.go index 85e3a6fc2cc..0a1715fa191 100644 --- a/common/client/send_only_node.go +++ b/common/client/send_only_node.go @@ -17,13 +17,13 @@ type sendOnlyClient[ ] interface { Close() ChainID(context.Context) (CHAIN_ID, error) - DialHTTP() error + Dial(ctx context.Context) error } // SendOnlyNode represents one node used as a sendonly type SendOnlyNode[ CHAIN_ID types.ID, - RPC sendOnlyClient[CHAIN_ID], + RPC any, ] interface { // Start may attempt to connect to the node, but should only return error for misconfiguration - never for temporary errors. Start(context.Context) error @@ -97,7 +97,7 @@ func (s *sendOnlyNode[CHAIN_ID, RPC]) start(startCtx context.Context) { panic(fmt.Sprintf("cannot dial node with state %v", s.state)) } - err := s.rpc.DialHTTP() + err := s.rpc.Dial(startCtx) if err != nil { promPoolRPCNodeTransitionsToUnusable.WithLabelValues(s.chainID.String(), s.name).Inc() s.log.Errorw("Dial failed: SendOnly Node is unusable", "err", err) diff --git a/common/client/send_only_node_test.go b/common/client/send_only_node_test.go index 79f4bfd60e3..532946da48f 100644 --- a/common/client/send_only_node_test.go +++ b/common/client/send_only_node_test.go @@ -46,7 +46,7 @@ func TestStartSendOnlyNode(t *testing.T) { client := newMockSendOnlyClient[types.ID](t) client.On("Close").Once() expectedError := errors.New("some http error") - client.On("DialHTTP").Return(expectedError).Once() + client.On("Dial", mock.Anything).Return(expectedError).Once() s := NewSendOnlyNode(lggr, url.URL{}, t.Name(), types.RandomID(), client) defer func() { assert.NoError(t, s.Close()) }() @@ -61,7 +61,7 @@ func TestStartSendOnlyNode(t *testing.T) { lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) client := newMockSendOnlyClient[types.ID](t) client.On("Close").Once() - client.On("DialHTTP").Return(nil).Once() + client.On("Dial", mock.Anything).Return(nil).Once() s := NewSendOnlyNode(lggr, url.URL{}, t.Name(), types.NewIDFromInt(0), client) defer func() { assert.NoError(t, s.Close()) }() @@ -76,7 +76,7 @@ func TestStartSendOnlyNode(t *testing.T) { lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) client := newMockSendOnlyClient[types.ID](t) client.On("Close").Once() - client.On("DialHTTP").Return(nil) + client.On("Dial", mock.Anything).Return(nil) expectedError := errors.New("failed to get chain ID") chainID := types.RandomID() const failuresCount = 2 @@ -100,7 +100,7 @@ func TestStartSendOnlyNode(t *testing.T) { lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) client := newMockSendOnlyClient[types.ID](t) client.On("Close").Once() - client.On("DialHTTP").Return(nil).Once() + client.On("Dial", mock.Anything).Return(nil).Once() configuredChainID := types.NewIDFromInt(11) rpcChainID := types.NewIDFromInt(20) const failuresCount = 2 @@ -123,7 +123,7 @@ func TestStartSendOnlyNode(t *testing.T) { lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) client := newMockSendOnlyClient[types.ID](t) client.On("Close").Once() - client.On("DialHTTP").Return(nil).Once() + client.On("Dial", mock.Anything).Return(nil).Once() configuredChainID := types.RandomID() client.On("ChainID", mock.Anything).Return(configuredChainID, nil) s := NewSendOnlyNode(lggr, url.URL{}, t.Name(), configuredChainID, client) diff --git a/common/client/transaction_sender.go b/common/client/transaction_sender.go new file mode 100644 index 00000000000..9365a82b290 --- /dev/null +++ b/common/client/transaction_sender.go @@ -0,0 +1,280 @@ +package client + +import ( + "context" + "errors" + "fmt" + "math" + "slices" + "sync" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/common/types" +) + +var ( + // PromMultiNodeInvariantViolations reports violation of our assumptions + PromMultiNodeInvariantViolations = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "multi_node_invariant_violations", + Help: "The number of invariant violations", + }, []string{"network", "chainId", "invariant"}) +) + +// TxErrorClassifier - defines interface of a function that transforms raw RPC error into the SendTxReturnCode enum +// (e.g. Successful, Fatal, Retryable, etc.) +type TxErrorClassifier[TX any] func(tx TX, err error) SendTxReturnCode + +type sendTxResult struct { + Err error + ResultCode SendTxReturnCode +} + +const sendTxQuorum = 0.7 + +// SendTxRPCClient - defines interface of an RPC used by TransactionSender to broadcast transaction +type SendTxRPCClient[TX any] interface { + // SendTransaction errors returned should include name or other unique identifier of the RPC + SendTransaction(ctx context.Context, tx TX) error +} + +func NewTransactionSender[TX any, CHAIN_ID types.ID, RPC SendTxRPCClient[TX]]( + lggr logger.Logger, + chainID CHAIN_ID, + chainFamily string, + multiNode *MultiNode[CHAIN_ID, RPC], + txErrorClassifier TxErrorClassifier[TX], + sendTxSoftTimeout time.Duration, +) *TransactionSender[TX, CHAIN_ID, RPC] { + if sendTxSoftTimeout == 0 { + sendTxSoftTimeout = QueryTimeout / 2 + } + return &TransactionSender[TX, CHAIN_ID, RPC]{ + chainID: chainID, + chainFamily: chainFamily, + lggr: logger.Sugared(lggr).Named("TransactionSender").With("chainID", chainID.String()), + multiNode: multiNode, + txErrorClassifier: txErrorClassifier, + sendTxSoftTimeout: sendTxSoftTimeout, + chStop: make(services.StopChan), + } +} + +type TransactionSender[TX any, CHAIN_ID types.ID, RPC SendTxRPCClient[TX]] struct { + services.StateMachine + chainID CHAIN_ID + chainFamily string + lggr logger.SugaredLogger + multiNode *MultiNode[CHAIN_ID, RPC] + txErrorClassifier TxErrorClassifier[TX] + sendTxSoftTimeout time.Duration // defines max waiting time from first response til responses evaluation + + wg sync.WaitGroup // waits for all reporting goroutines to finish + chStop services.StopChan +} + +// SendTransaction - broadcasts transaction to all the send-only and primary nodes in MultiNode. +// A returned nil or error does not guarantee that the transaction will or won't be included. Additional checks must be +// performed to determine the final state. +// +// Send-only nodes' results are ignored as they tend to return false-positive responses. Broadcast to them is necessary +// to speed up the propagation of TX in the network. +// +// Handling of primary nodes' results consists of collection and aggregation. +// In the collection step, we gather as many results as possible while minimizing waiting time. This operation succeeds +// on one of the following conditions: +// * Received at least one success +// * Received at least one result and `sendTxSoftTimeout` expired +// * Received results from the sufficient number of nodes defined by sendTxQuorum. +// The aggregation is based on the following conditions: +// * If there is at least one success - returns success +// * If there is at least one terminal error - returns terminal error +// * If there is both success and terminal error - returns success and reports invariant violation +// * Otherwise, returns any (effectively random) of the errors. +func (txSender *TransactionSender[TX, CHAIN_ID, RPC]) SendTransaction(ctx context.Context, tx TX) (SendTxReturnCode, error) { + txResults := make(chan sendTxResult) + txResultsToReport := make(chan sendTxResult) + primaryNodeWg := sync.WaitGroup{} + + if txSender.State() != "Started" { + return Retryable, errors.New("TransactionSender not started") + } + + healthyNodesNum := 0 + err := txSender.multiNode.DoAll(ctx, func(ctx context.Context, rpc RPC, isSendOnly bool) { + if isSendOnly { + txSender.wg.Add(1) + go func() { + defer txSender.wg.Done() + // Send-only nodes' results are ignored as they tend to return false-positive responses. + // Broadcast to them is necessary to speed up the propagation of TX in the network. + _ = txSender.broadcastTxAsync(ctx, rpc, tx) + }() + return + } + + // Primary Nodes + healthyNodesNum++ + primaryNodeWg.Add(1) + go func() { + defer primaryNodeWg.Done() + result := txSender.broadcastTxAsync(ctx, rpc, tx) + select { + case <-ctx.Done(): + return + case txResults <- result: + } + + select { + case <-ctx.Done(): + return + case txResultsToReport <- result: + } + }() + }) + + // This needs to be done in parallel so the reporting knows when it's done (when the channel is closed) + txSender.wg.Add(1) + go func() { + defer txSender.wg.Done() + primaryNodeWg.Wait() + close(txResultsToReport) + close(txResults) + }() + + if err != nil { + return Retryable, err + } + + txSender.wg.Add(1) + go txSender.reportSendTxAnomalies(tx, txResultsToReport) + + return txSender.collectTxResults(ctx, tx, healthyNodesNum, txResults) +} + +func (txSender *TransactionSender[TX, CHAIN_ID, RPC]) broadcastTxAsync(ctx context.Context, rpc RPC, tx TX) sendTxResult { + txErr := rpc.SendTransaction(ctx, tx) + txSender.lggr.Debugw("Node sent transaction", "tx", tx, "err", txErr) + resultCode := txSender.txErrorClassifier(tx, txErr) + if !slices.Contains(sendTxSuccessfulCodes, resultCode) { + txSender.lggr.Warnw("RPC returned error", "tx", tx, "err", txErr) + } + return sendTxResult{Err: txErr, ResultCode: resultCode} +} + +func (txSender *TransactionSender[TX, CHAIN_ID, RPC]) reportSendTxAnomalies(tx TX, txResults <-chan sendTxResult) { + defer txSender.wg.Done() + resultsByCode := sendTxResults{} + // txResults eventually will be closed + for txResult := range txResults { + resultsByCode[txResult.ResultCode] = append(resultsByCode[txResult.ResultCode], txResult.Err) + } + + _, _, criticalErr := aggregateTxResults(resultsByCode) + if criticalErr != nil { + txSender.lggr.Criticalw("observed invariant violation on SendTransaction", "tx", tx, "resultsByCode", resultsByCode, "err", criticalErr) + PromMultiNodeInvariantViolations.WithLabelValues(txSender.chainFamily, txSender.chainID.String(), criticalErr.Error()).Inc() + } +} + +type sendTxResults map[SendTxReturnCode][]error + +func aggregateTxResults(resultsByCode sendTxResults) (returnCode SendTxReturnCode, txResult error, err error) { + severeCode, severeErrors, hasSevereErrors := findFirstIn(resultsByCode, sendTxSevereErrors) + successCode, successResults, hasSuccess := findFirstIn(resultsByCode, sendTxSuccessfulCodes) + if hasSuccess { + // We assume that primary node would never report false positive txResult for a transaction. + // Thus, if such case occurs it's probably due to misconfiguration or a bug and requires manual intervention. + if hasSevereErrors { + const errMsg = "found contradictions in nodes replies on SendTransaction: got success and severe error" + // return success, since at least 1 node has accepted our broadcasted Tx, and thus it can now be included onchain + return successCode, successResults[0], errors.New(errMsg) + } + + // other errors are temporary - we are safe to return success + return successCode, successResults[0], nil + } + + if hasSevereErrors { + return severeCode, severeErrors[0], nil + } + + // return temporary error + for code, result := range resultsByCode { + return code, result[0], nil + } + + err = fmt.Errorf("expected at least one response on SendTransaction") + return Retryable, err, err +} + +func (txSender *TransactionSender[TX, CHAIN_ID, RPC]) collectTxResults(ctx context.Context, tx TX, healthyNodesNum int, txResults <-chan sendTxResult) (SendTxReturnCode, error) { + if healthyNodesNum == 0 { + return Retryable, ErroringNodeError + } + ctx, cancel := txSender.chStop.Ctx(ctx) + defer cancel() + requiredResults := int(math.Ceil(float64(healthyNodesNum) * sendTxQuorum)) + errorsByCode := sendTxResults{} + var softTimeoutChan <-chan time.Time + var resultsCount int +loop: + for { + select { + case <-ctx.Done(): + txSender.lggr.Debugw("Failed to collect of the results before context was done", "tx", tx, "errorsByCode", errorsByCode) + return Retryable, ctx.Err() + case result := <-txResults: + errorsByCode[result.ResultCode] = append(errorsByCode[result.ResultCode], result.Err) + resultsCount++ + if slices.Contains(sendTxSuccessfulCodes, result.ResultCode) || resultsCount >= requiredResults { + break loop + } + case <-softTimeoutChan: + txSender.lggr.Debugw("Send Tx soft timeout expired - returning responses we've collected so far", "tx", tx, "resultsCount", resultsCount, "requiredResults", requiredResults) + break loop + } + + if softTimeoutChan == nil { + tm := time.NewTimer(txSender.sendTxSoftTimeout) + softTimeoutChan = tm.C + // we are fine with stopping timer at the end of function + //nolint + defer tm.Stop() + } + } + + // ignore critical error as it's reported in reportSendTxAnomalies + returnCode, result, _ := aggregateTxResults(errorsByCode) + return returnCode, result +} + +func (txSender *TransactionSender[TX, CHAIN_ID, RPC]) Start(ctx context.Context) error { + return txSender.StartOnce("TransactionSender", func() error { + return nil + }) +} + +func (txSender *TransactionSender[TX, CHAIN_ID, RPC]) Close() error { + return txSender.StopOnce("TransactionSender", func() error { + close(txSender.chStop) + txSender.wg.Wait() + return nil + }) +} + +// findFirstIn - returns the first existing key and value for the slice of keys +func findFirstIn[K comparable, V any](set map[K]V, keys []K) (K, V, bool) { + for _, k := range keys { + if v, ok := set[k]; ok { + return k, v, true + } + } + var zeroK K + var zeroV V + return zeroK, zeroV, false +} diff --git a/common/client/transaction_sender_test.go b/common/client/transaction_sender_test.go new file mode 100644 index 00000000000..5517a0c8dda --- /dev/null +++ b/common/client/transaction_sender_test.go @@ -0,0 +1,384 @@ +package client + +import ( + "context" + "fmt" + "testing" + + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink/v2/common/types" +) + +type sendTxMultiNode struct { + *MultiNode[types.ID, SendTxRPCClient[any]] +} + +type sendTxRPC struct { + sendTxRun func(args mock.Arguments) + sendTxErr error +} + +var _ SendTxRPCClient[any] = (*sendTxRPC)(nil) + +func newSendTxRPC(sendTxErr error, sendTxRun func(args mock.Arguments)) *sendTxRPC { + return &sendTxRPC{sendTxErr: sendTxErr, sendTxRun: sendTxRun} +} + +func (rpc *sendTxRPC) SendTransaction(ctx context.Context, _ any) error { + if rpc.sendTxRun != nil { + rpc.sendTxRun(mock.Arguments{ctx}) + } + return rpc.sendTxErr +} + +func newTestTransactionSender(t *testing.T, chainID types.ID, lggr logger.Logger, + nodes []Node[types.ID, SendTxRPCClient[any]], + sendOnlyNodes []SendOnlyNode[types.ID, SendTxRPCClient[any]], +) (*sendTxMultiNode, *TransactionSender[any, types.ID, SendTxRPCClient[any]]) { + mn := sendTxMultiNode{NewMultiNode[types.ID, SendTxRPCClient[any]]( + lggr, NodeSelectionModeRoundRobin, 0, nodes, sendOnlyNodes, chainID, "chainFamily", 0)} + err := mn.StartOnce("startedTestMultiNode", func() error { return nil }) + require.NoError(t, err) + + txSender := NewTransactionSender[any, types.ID, SendTxRPCClient[any]](lggr, chainID, mn.chainFamily, mn.MultiNode, classifySendTxError, tests.TestInterval) + err = txSender.Start(tests.Context(t)) + require.NoError(t, err) + + t.Cleanup(func() { + err := mn.Close() + if err != nil { + // Allow MultiNode to be closed early for testing + require.EqualError(t, err, "MultiNode has already been stopped: already stopped") + } + err = txSender.Close() + if err != nil { + // Allow TransactionSender to be closed early for testing + require.EqualError(t, err, "TransactionSender has already been stopped: already stopped") + } + }) + return &mn, txSender +} + +func classifySendTxError(_ any, err error) SendTxReturnCode { + if err != nil { + return Fatal + } + return Successful +} + +func TestTransactionSender_SendTransaction(t *testing.T) { + t.Parallel() + + newNodeWithState := func(t *testing.T, state nodeState, txErr error, sendTxRun func(args mock.Arguments)) *mockNode[types.ID, SendTxRPCClient[any]] { + rpc := newSendTxRPC(txErr, sendTxRun) + node := newMockNode[types.ID, SendTxRPCClient[any]](t) + node.On("String").Return("node name").Maybe() + node.On("RPC").Return(rpc).Maybe() + node.On("State").Return(state).Maybe() + node.On("Close").Return(nil).Once() + return node + } + + newNode := func(t *testing.T, txErr error, sendTxRun func(args mock.Arguments)) *mockNode[types.ID, SendTxRPCClient[any]] { + return newNodeWithState(t, nodeStateAlive, txErr, sendTxRun) + } + + t.Run("Fails if there is no nodes available", func(t *testing.T) { + lggr, _ := logger.TestObserved(t, zap.DebugLevel) + _, txSender := newTestTransactionSender(t, types.RandomID(), lggr, nil, nil) + _, err := txSender.SendTransaction(tests.Context(t), nil) + assert.EqualError(t, err, ErroringNodeError.Error()) + }) + + t.Run("Transaction failure happy path", func(t *testing.T) { + expectedError := errors.New("transaction failed") + mainNode := newNode(t, expectedError, nil) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) + + _, txSender := newTestTransactionSender(t, types.RandomID(), lggr, + []Node[types.ID, SendTxRPCClient[any]]{mainNode}, + []SendOnlyNode[types.ID, SendTxRPCClient[any]]{newNode(t, errors.New("unexpected error"), nil)}) + + result, sendErr := txSender.SendTransaction(tests.Context(t), nil) + require.ErrorIs(t, sendErr, expectedError) + require.Equal(t, Fatal, result) + tests.AssertLogCountEventually(t, observedLogs, "Node sent transaction", 2) + tests.AssertLogCountEventually(t, observedLogs, "RPC returned error", 2) + }) + + t.Run("Transaction success happy path", func(t *testing.T) { + mainNode := newNode(t, nil, nil) + + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) + _, txSender := newTestTransactionSender(t, types.RandomID(), lggr, + []Node[types.ID, SendTxRPCClient[any]]{mainNode}, + []SendOnlyNode[types.ID, SendTxRPCClient[any]]{newNode(t, errors.New("unexpected error"), nil)}) + + result, sendErr := txSender.SendTransaction(tests.Context(t), nil) + require.NoError(t, sendErr) + require.Equal(t, Successful, result) + tests.AssertLogCountEventually(t, observedLogs, "Node sent transaction", 2) + tests.AssertLogCountEventually(t, observedLogs, "RPC returned error", 1) + }) + + t.Run("Context expired before collecting sufficient results", func(t *testing.T) { + testContext, testCancel := context.WithCancel(tests.Context(t)) + defer testCancel() + + mainNode := newNode(t, nil, func(_ mock.Arguments) { + // block caller til end of the test + <-testContext.Done() + }) + + lggr, _ := logger.TestObserved(t, zap.DebugLevel) + + _, txSender := newTestTransactionSender(t, types.RandomID(), lggr, + []Node[types.ID, SendTxRPCClient[any]]{mainNode}, nil) + + requestContext, cancel := context.WithCancel(tests.Context(t)) + cancel() + _, sendErr := txSender.SendTransaction(requestContext, nil) + require.EqualError(t, sendErr, "context canceled") + }) + + t.Run("Soft timeout stops results collection", func(t *testing.T) { + chainID := types.RandomID() + expectedError := errors.New("transaction failed") + fastNode := newNode(t, expectedError, nil) + + // hold reply from the node till end of the test + testContext, testCancel := context.WithCancel(tests.Context(t)) + defer testCancel() + slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) { + // block caller til end of the test + <-testContext.Done() + }) + + lggr, _ := logger.TestObserved(t, zap.DebugLevel) + + _, txSender := newTestTransactionSender(t, chainID, lggr, []Node[types.ID, SendTxRPCClient[any]]{fastNode, slowNode}, nil) + _, sendErr := txSender.SendTransaction(tests.Context(t), nil) + require.EqualError(t, sendErr, expectedError.Error()) + }) + t.Run("Returns success without waiting for the rest of the nodes", func(t *testing.T) { + chainID := types.RandomID() + fastNode := newNode(t, nil, nil) + // hold reply from the node till end of the test + testContext, testCancel := context.WithCancel(tests.Context(t)) + defer testCancel() + slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) { + // block caller til end of the test + <-testContext.Done() + }) + slowSendOnly := newNode(t, errors.New("send only failed"), func(_ mock.Arguments) { + // block caller til end of the test + <-testContext.Done() + }) + lggr, _ := logger.TestObserved(t, zap.WarnLevel) + mn, txSender := newTestTransactionSender(t, chainID, lggr, + []Node[types.ID, SendTxRPCClient[any]]{fastNode, slowNode}, + []SendOnlyNode[types.ID, SendTxRPCClient[any]]{slowSendOnly}) + + rtnCode, err := txSender.SendTransaction(tests.Context(t), nil) + require.NoError(t, err) + require.Equal(t, Successful, rtnCode) + require.NoError(t, mn.Close()) + }) + t.Run("Fails when multinode is closed", func(t *testing.T) { + chainID := types.RandomID() + fastNode := newNode(t, nil, nil) + // hold reply from the node till end of the test + testContext, testCancel := context.WithCancel(tests.Context(t)) + defer testCancel() + slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) { + // block caller til end of the test + <-testContext.Done() + }) + slowSendOnly := newNode(t, errors.New("send only failed"), func(_ mock.Arguments) { + // block caller til end of the test + <-testContext.Done() + }) + + lggr, _ := logger.TestObserved(t, zap.DebugLevel) + + mn, txSender := newTestTransactionSender(t, chainID, lggr, + []Node[types.ID, SendTxRPCClient[any]]{fastNode, slowNode}, + []SendOnlyNode[types.ID, SendTxRPCClient[any]]{slowSendOnly}) + + require.NoError(t, mn.Close()) + _, err := txSender.SendTransaction(tests.Context(t), nil) + require.EqualError(t, err, "MultiNode is stopped") + }) + t.Run("Fails when closed", func(t *testing.T) { + chainID := types.RandomID() + fastNode := newNode(t, nil, nil) + // hold reply from the node till end of the test + testContext, testCancel := context.WithCancel(tests.Context(t)) + defer testCancel() + slowNode := newNode(t, errors.New("transaction failed"), func(_ mock.Arguments) { + // block caller til end of the test + <-testContext.Done() + }) + slowSendOnly := newNode(t, errors.New("send only failed"), func(_ mock.Arguments) { + // block caller til end of the test + <-testContext.Done() + }) + + lggr, _ := logger.TestObserved(t, zap.DebugLevel) + + _, txSender := newTestTransactionSender(t, chainID, lggr, + []Node[types.ID, SendTxRPCClient[any]]{fastNode, slowNode}, + []SendOnlyNode[types.ID, SendTxRPCClient[any]]{slowSendOnly}) + + require.NoError(t, txSender.Close()) + _, err := txSender.SendTransaction(tests.Context(t), nil) + require.EqualError(t, err, "TransactionSender not started") + }) + t.Run("Returns error if there is no healthy primary nodes", func(t *testing.T) { + chainID := types.RandomID() + primary := newNodeWithState(t, nodeStateUnreachable, nil, nil) + sendOnly := newNodeWithState(t, nodeStateUnreachable, nil, nil) + + lggr, _ := logger.TestObserved(t, zap.DebugLevel) + + _, txSender := newTestTransactionSender(t, chainID, lggr, + []Node[types.ID, SendTxRPCClient[any]]{primary}, + []SendOnlyNode[types.ID, SendTxRPCClient[any]]{sendOnly}) + + _, sendErr := txSender.SendTransaction(tests.Context(t), nil) + assert.EqualError(t, sendErr, ErroringNodeError.Error()) + }) + + t.Run("Transaction success even if one of the nodes is unhealthy", func(t *testing.T) { + chainID := types.RandomID() + mainNode := newNode(t, nil, nil) + unexpectedCall := func(args mock.Arguments) { + panic("SendTx must not be called for unhealthy node") + } + unhealthyNode := newNodeWithState(t, nodeStateUnreachable, nil, unexpectedCall) + unhealthySendOnlyNode := newNodeWithState(t, nodeStateUnreachable, nil, unexpectedCall) + + lggr, _ := logger.TestObserved(t, zap.DebugLevel) + + _, txSender := newTestTransactionSender(t, chainID, lggr, + []Node[types.ID, SendTxRPCClient[any]]{mainNode, unhealthyNode}, + []SendOnlyNode[types.ID, SendTxRPCClient[any]]{unhealthySendOnlyNode}) + + returnCode, sendErr := txSender.SendTransaction(tests.Context(t), nil) + require.NoError(t, sendErr) + require.Equal(t, Successful, returnCode) + }) +} + +func TestTransactionSender_SendTransaction_aggregateTxResults(t *testing.T) { + t.Parallel() + // ensure failure on new SendTxReturnCode + codesToCover := map[SendTxReturnCode]struct{}{} + for code := Successful; code < sendTxReturnCodeLen; code++ { + codesToCover[code] = struct{}{} + } + + testCases := []struct { + Name string + ExpectedTxResult string + ExpectedCriticalErr string + ResultsByCode sendTxResults + }{ + { + Name: "Returns success and logs critical error on success and Fatal", + ExpectedTxResult: "success", + ExpectedCriticalErr: "found contradictions in nodes replies on SendTransaction: got success and severe error", + ResultsByCode: sendTxResults{ + Successful: {errors.New("success")}, + Fatal: {errors.New("fatal")}, + }, + }, + { + Name: "Returns TransactionAlreadyKnown and logs critical error on TransactionAlreadyKnown and Fatal", + ExpectedTxResult: "tx_already_known", + ExpectedCriticalErr: "found contradictions in nodes replies on SendTransaction: got success and severe error", + ResultsByCode: sendTxResults{ + TransactionAlreadyKnown: {errors.New("tx_already_known")}, + Unsupported: {errors.New("unsupported")}, + }, + }, + { + Name: "Prefers sever error to temporary", + ExpectedTxResult: "underpriced", + ExpectedCriticalErr: "", + ResultsByCode: sendTxResults{ + Retryable: {errors.New("retryable")}, + Underpriced: {errors.New("underpriced")}, + }, + }, + { + Name: "Returns temporary error", + ExpectedTxResult: "retryable", + ExpectedCriticalErr: "", + ResultsByCode: sendTxResults{ + Retryable: {errors.New("retryable")}, + }, + }, + { + Name: "Insufficient funds is treated as error", + ExpectedTxResult: "", + ExpectedCriticalErr: "", + ResultsByCode: sendTxResults{ + Successful: {nil}, + InsufficientFunds: {errors.New("insufficientFunds")}, + }, + }, + { + Name: "Logs critical error on empty ResultsByCode", + ExpectedTxResult: "expected at least one response on SendTransaction", + ExpectedCriticalErr: "expected at least one response on SendTransaction", + ResultsByCode: sendTxResults{}, + }, + { + Name: "Zk terminally stuck", + ExpectedTxResult: "not enough keccak counters to continue the execution", + ExpectedCriticalErr: "", + ResultsByCode: sendTxResults{ + TerminallyStuck: {errors.New("not enough keccak counters to continue the execution")}, + }, + }, + } + + for _, testCase := range testCases { + for code := range testCase.ResultsByCode { + delete(codesToCover, code) + } + + t.Run(testCase.Name, func(t *testing.T) { + _, txResult, err := aggregateTxResults(testCase.ResultsByCode) + if testCase.ExpectedTxResult == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, txResult, testCase.ExpectedTxResult) + } + + logger.Sugared(logger.Test(t)).Info("Map: " + fmt.Sprint(testCase.ResultsByCode)) + logger.Sugared(logger.Test(t)).Criticalw("observed invariant violation on SendTransaction", "resultsByCode", testCase.ResultsByCode, "err", err) + + if testCase.ExpectedCriticalErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, testCase.ExpectedCriticalErr) + } + }) + } + + // explicitly signal that following codes are properly handled in aggregateTxResults, + // but dedicated test cases won't be beneficial + for _, codeToIgnore := range []SendTxReturnCode{Unknown, ExceedsMaxFee, FeeOutOfValidRange} { + delete(codesToCover, codeToIgnore) + } + assert.Empty(t, codesToCover, "all of the SendTxReturnCode must be covered by this test") +} diff --git a/common/client/types.go b/common/client/types.go index c9b6a3580eb..38880397442 100644 --- a/common/client/types.go +++ b/common/client/types.go @@ -4,71 +4,30 @@ import ( "context" "math/big" - "github.com/smartcontractkit/chainlink-common/pkg/assets" - - feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" ) -// RPC includes all the necessary methods for a multi-node client to interact directly with any RPC endpoint. -type RPC[ - CHAIN_ID types.ID, - SEQ types.Sequence, - ADDR types.Hashable, - BLOCK_HASH types.Hashable, - TX any, - TX_HASH types.Hashable, - EVENT any, - EVENT_OPS any, - TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], - FEE feetypes.Fee, - HEAD types.Head[BLOCK_HASH], - BATCH_ELEM any, -] interface { - NodeClient[ - CHAIN_ID, - HEAD, - ] - clientAPI[ - CHAIN_ID, - SEQ, - ADDR, - BLOCK_HASH, - TX, - TX_HASH, - EVENT, - EVENT_OPS, - TX_RECEIPT, - FEE, - HEAD, - BATCH_ELEM, - ] -} - -// Head is the interface required by the NodeClient -type Head interface { - BlockNumber() int64 - BlockDifficulty() *big.Int - IsValid() bool -} - -// NodeClient includes all the necessary RPC methods required by a node. -type NodeClient[ +// RPCClient includes all the necessary generalized RPC methods used by Node to perform health checks +type RPCClient[ CHAIN_ID types.ID, HEAD Head, ] interface { - connection[CHAIN_ID, HEAD] - - DialHTTP() error - // DisconnectAll - cancels all inflight requests, terminates all subscriptions and resets latest ChainInfo. - DisconnectAll() - Close() - ClientVersion(context.Context) (string, error) - SubscribersCount() int32 - SetAliveLoopSub(types.Subscription) - UnsubscribeAllExceptAliveLoop() + // ChainID - fetches ChainID from the RPC to verify that it matches config + ChainID(ctx context.Context) (CHAIN_ID, error) + // Dial - prepares the RPC for usage. Can be called on fresh or closed RPC + Dial(ctx context.Context) error + // SubscribeToHeads - returns channel and subscription for new heads. + SubscribeToHeads(ctx context.Context) (<-chan HEAD, types.Subscription, error) + // SubscribeToFinalizedHeads - returns channel and subscription for finalized heads. + SubscribeToFinalizedHeads(ctx context.Context) (<-chan HEAD, types.Subscription, error) + // Ping - returns error if RPC is not reachable + Ping(context.Context) error + // IsSyncing - returns true if the RPC is in Syncing state and can not process calls IsSyncing(ctx context.Context) (bool, error) - SubscribeToFinalizedHeads(_ context.Context) (<-chan HEAD, types.Subscription, error) + // UnsubscribeAllExcept - close all subscriptions except `subs` + UnsubscribeAllExcept(subs ...types.Subscription) + // Close - closes all subscriptions and aborts all RPC calls + Close() // GetInterceptedChainInfo - returns latest and highest observed by application layer ChainInfo. // latest ChainInfo is the most recent value received within a NodeClient's current lifecycle between Dial and DisconnectAll. // highestUserObservations ChainInfo is the highest ChainInfo observed excluding health checks calls. @@ -82,78 +41,11 @@ type NodeClient[ GetInterceptedChainInfo() (latest, highestUserObservations ChainInfo) } -// clientAPI includes all the direct RPC methods required by the generalized common client to implement its own. -type clientAPI[ - CHAIN_ID types.ID, - SEQ types.Sequence, - ADDR types.Hashable, - BLOCK_HASH types.Hashable, - TX any, - TX_HASH types.Hashable, - EVENT any, - EVENT_OPS any, // event filter query options - TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], - FEE feetypes.Fee, - HEAD types.Head[BLOCK_HASH], - BATCH_ELEM any, -] interface { - connection[CHAIN_ID, HEAD] - - // Account - BalanceAt(ctx context.Context, accountAddress ADDR, blockNumber *big.Int) (*big.Int, error) - TokenBalance(ctx context.Context, accountAddress ADDR, tokenAddress ADDR) (*big.Int, error) - SequenceAt(ctx context.Context, accountAddress ADDR, blockNumber *big.Int) (SEQ, error) - LINKBalance(ctx context.Context, accountAddress ADDR, linkAddress ADDR) (*assets.Link, error) - PendingSequenceAt(ctx context.Context, addr ADDR) (SEQ, error) - EstimateGas(ctx context.Context, call any) (gas uint64, err error) - - // Transactions - SendTransaction(ctx context.Context, tx TX) error - SimulateTransaction(ctx context.Context, tx TX) error - TransactionByHash(ctx context.Context, txHash TX_HASH) (TX, error) - TransactionReceipt(ctx context.Context, txHash TX_HASH) (TX_RECEIPT, error) - SendEmptyTransaction( - ctx context.Context, - newTxAttempt func(seq SEQ, feeLimit uint32, fee FEE, fromAddress ADDR) (attempt any, err error), - seq SEQ, - gasLimit uint32, - fee FEE, - fromAddress ADDR, - ) (txhash string, err error) - - // Blocks - BlockByNumber(ctx context.Context, number *big.Int) (HEAD, error) - BlockByHash(ctx context.Context, hash BLOCK_HASH) (HEAD, error) - LatestBlockHeight(context.Context) (*big.Int, error) - LatestFinalizedBlock(ctx context.Context) (HEAD, error) - - // Events - FilterEvents(ctx context.Context, query EVENT_OPS) ([]EVENT, error) - - // Misc - BatchCallContext(ctx context.Context, b []BATCH_ELEM) error - CallContract( - ctx context.Context, - msg interface{}, - blockNumber *big.Int, - ) (rpcErr []byte, extractErr error) - PendingCallContract( - ctx context.Context, - msg interface{}, - ) (rpcErr []byte, extractErr error) - CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error - CodeAt(ctx context.Context, account ADDR, blockNumber *big.Int) ([]byte, error) -} - -type connection[ - CHAIN_ID types.ID, - HEAD Head, -] interface { - ChainID(ctx context.Context) (CHAIN_ID, error) - Dial(ctx context.Context) error - SubscribeToHeads(ctx context.Context) (ch <-chan HEAD, sub types.Subscription, err error) - // TODO: remove as part of merge with BCI-2875 - SubscribeNewHead(ctx context.Context, channel chan<- HEAD) (s types.Subscription, err error) +// Head is the interface required by the NodeClient +type Head interface { + BlockNumber() int64 + BlockDifficulty() *big.Int + IsValid() bool } // PoolChainInfoProvider - provides aggregation of nodes pool ChainInfo diff --git a/common/headtracker/head_listener.go b/common/headtracker/head_listener.go index d240caab3c3..5f967706e04 100644 --- a/common/headtracker/head_listener.go +++ b/common/headtracker/head_listener.go @@ -62,7 +62,7 @@ type headListener[ client htrktypes.Client[HTH, S, ID, BLOCK_HASH] onSubscription func(context.Context) handleNewHead HeadHandler[HTH, BLOCK_HASH] - chHeaders chan HTH + chHeaders <-chan HTH headSubscription types.Subscription connected atomic.Bool receivingHeads atomic.Bool @@ -227,13 +227,10 @@ func (hl *headListener[HTH, S, ID, BLOCK_HASH]) subscribe(ctx context.Context) b } func (hl *headListener[HTH, S, ID, BLOCK_HASH]) subscribeToHead(ctx context.Context) error { - hl.chHeaders = make(chan HTH) - var err error - hl.headSubscription, err = hl.client.SubscribeNewHead(ctx, hl.chHeaders) + hl.chHeaders, hl.headSubscription, err = hl.client.SubscribeToHeads(ctx) if err != nil { - close(hl.chHeaders) - return fmt.Errorf("Client#SubscribeNewHead: %w", err) + return fmt.Errorf("Client#SubscribeToHeads: %w", err) } hl.connected.Store(true) diff --git a/common/headtracker/types/client.go b/common/headtracker/types/client.go index a1e419809b5..e1b7aad08df 100644 --- a/common/headtracker/types/client.go +++ b/common/headtracker/types/client.go @@ -12,9 +12,9 @@ type Client[H types.Head[BLOCK_HASH], S types.Subscription, ID types.ID, BLOCK_H HeadByHash(ctx context.Context, hash BLOCK_HASH) (head H, err error) // ConfiguredChainID returns the chain ID that the node is configured to connect to ConfiguredChainID() (id ID) - // SubscribeNewHead is the method in which the client receives new Head. + // SubscribeToHeads is the method in which the client receives new Head. // It can be implemented differently for each chain i.e websocket, polling, etc - SubscribeNewHead(ctx context.Context, ch chan<- H) (S, error) + SubscribeToHeads(ctx context.Context) (<-chan H, S, error) // LatestFinalizedBlock - returns the latest block that was marked as finalized LatestFinalizedBlock(ctx context.Context) (head H, err error) } diff --git a/common/types/chain.go b/common/types/chain.go index 800f0d9fdc0..bf4654142a8 100644 --- a/common/types/chain.go +++ b/common/types/chain.go @@ -1,6 +1,10 @@ package types -import "fmt" +import ( + "fmt" + + "github.com/smartcontractkit/chainlink-common/pkg/types" +) // Sequence represents the base type, for any chain's sequence object. // It should be convertible to a string @@ -12,3 +16,10 @@ type Sequence interface { // ID represents the base type, for any chain's ID. // It should be convertible to a string, that can uniquely identify this chain type ID fmt.Stringer + +// ChainStatusWithID compose of ChainStatus and RelayID. This is useful for +// storing the Network associated with the ChainStatus. +type ChainStatusWithID struct { + types.ChainStatus + types.RelayID +} diff --git a/common/types/subscription.go b/common/types/subscription.go index b9b44e41943..3c4fd4c39f1 100644 --- a/common/types/subscription.go +++ b/common/types/subscription.go @@ -5,7 +5,8 @@ package types // This is a generic interface for Subscription to represent used by clients. type Subscription interface { // Unsubscribe cancels the sending of events to the data channel - // and closes the error channel. + // and closes the error channel. Unsubscribe should be callable multiple + // times without causing an error. Unsubscribe() // Err returns the subscription error channel. The error channel receives // a value if there is an issue with the subscription (e.g. the network connection diff --git a/contracts/.changeset/sharp-avocados-arrive.md b/contracts/.changeset/sharp-avocados-arrive.md new file mode 100644 index 00000000000..6bfd0524a88 --- /dev/null +++ b/contracts/.changeset/sharp-avocados-arrive.md @@ -0,0 +1,8 @@ +--- +'@chainlink/contracts': patch +--- + +#internal remove CCIP 1.5 + + +PR issue: CCIP-3748 \ No newline at end of file diff --git a/contracts/.changeset/silent-houses-join.md b/contracts/.changeset/silent-houses-join.md new file mode 100644 index 00000000000..4138756c78a --- /dev/null +++ b/contracts/.changeset/silent-houses-join.md @@ -0,0 +1,5 @@ +--- +'@chainlink/contracts': patch +--- + +Enable rotating encryptionPublicKey in CapabilitiesRegistry contract diff --git a/contracts/.changeset/tidy-kings-itch.md b/contracts/.changeset/tidy-kings-itch.md new file mode 100644 index 00000000000..e493a6cc551 --- /dev/null +++ b/contracts/.changeset/tidy-kings-itch.md @@ -0,0 +1,5 @@ +--- +'@chainlink/contracts': patch +--- + +#internal minor keystone improvements diff --git a/contracts/.solhintignore b/contracts/.solhintignore index d076b14dc6a..81291fe0871 100644 --- a/contracts/.solhintignore +++ b/contracts/.solhintignore @@ -36,7 +36,6 @@ ./src/v0.8/llo-feeds/test ./src/v0.8/vrf/testhelpers ./src/v0.8/functions/tests -./src/v0.8/ccip/test # Always ignore vendor ./src/v0.8/vendor diff --git a/contracts/STYLE_GUIDE.md b/contracts/STYLE_GUIDE.md index 0f1370be3d7..89fe0dd5efa 100644 --- a/contracts/STYLE_GUIDE.md +++ b/contracts/STYLE_GUIDE.md @@ -16,7 +16,8 @@ We will be looking into `forge fmt`, but for now, we still use `prettier`. # Guidelines ## Code Organization -- Group functionality together. E.g. Declare structs, events, and helper functions near the functions that use them. This is helpful when reading code because the related pieces are localized. It is also consistent with inheritance and libraries, which are separate pieces of code designed for a specific goal. +- Structs, events and custom errors should be defined at the top of the contract. +- Group helper functions near the functions that use them. This is helpful when reading code because the related pieces are localized. - 🤔Why not follow the Solidity recommendation of grouping by visibility? Visibility is clearly defined next to the method signature, making it trivial to check. However, searching can be deceiving because of inherited methods. Given this inconsistency in grouping, we find it easier to read and more consistent to organize code around functionality. Additionally, we recommend testing the public interface for any Solidity contract to ensure it only exposes expected methods. - Follow the [Solidity folder structure CLIP](https://github.com/smartcontractkit/CLIPs/tree/main/clips/2023-04-13-solidity-folder-structure) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index 753e63ca8fc..f61e72be3a0 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -2,23 +2,8 @@ ARMProxyStandaloneTest:test_ARMCallEmptyContractRevert() (gas: 19675) ARMProxyStandaloneTest:test_Constructor() (gas: 310043) ARMProxyStandaloneTest:test_SetARM() (gas: 16587) ARMProxyStandaloneTest:test_SetARMzero() (gas: 11297) -ARMProxyTest:test_ARMCallRevertReasonForwarded() (gas: 47898) -ARMProxyTest:test_ARMIsBlessed_Success() (gas: 36363) -ARMProxyTest:test_ARMIsCursed_Success() (gas: 49851) -AggregateTokenLimiter_constructor:test_Constructor_Success() (gas: 27118) -AggregateTokenLimiter_getTokenBucket:test_GetTokenBucket_Success() (gas: 19871) -AggregateTokenLimiter_getTokenBucket:test_Refill_Success() (gas: 41586) -AggregateTokenLimiter_getTokenBucket:test_TimeUnderflow_Revert() (gas: 15452) -AggregateTokenLimiter_getTokenLimitAdmin:test_GetTokenLimitAdmin_Success() (gas: 10537) -AggregateTokenLimiter_getTokenValue:test_GetTokenValue_Success() (gas: 17531) -AggregateTokenLimiter_getTokenValue:test_NoTokenPrice_Reverts() (gas: 21414) -AggregateTokenLimiter_rateLimitValue:test_AggregateValueMaxCapacityExceeded_Revert() (gas: 16586) -AggregateTokenLimiter_rateLimitValue:test_RateLimitValueSuccess_gas() (gas: 18357) -AggregateTokenLimiter_setAdmin:test_OnlyOwnerOrAdmin_Revert() (gas: 13078) -AggregateTokenLimiter_setAdmin:test_Owner_Success() (gas: 19016) -AggregateTokenLimiter_setRateLimiterConfig:test_OnlyOnlyCallableByAdminOrOwner_Revert() (gas: 17546) -AggregateTokenLimiter_setRateLimiterConfig:test_Owner_Success() (gas: 30393) -AggregateTokenLimiter_setRateLimiterConfig:test_TokenLimitAdmin_Success() (gas: 32407) +ARMProxyTest:test_ARMCallRevertReasonForwarded() (gas: 45141) +ARMProxyTest:test_ARMIsCursed_Success() (gas: 47106) BurnFromMintTokenPool_lockOrBurn:test_ChainNotAllowed_Revert() (gas: 28842) BurnFromMintTokenPool_lockOrBurn:test_PoolBurnRevertNotHealthy_Revert() (gas: 55271) BurnFromMintTokenPool_lockOrBurn:test_PoolBurn_Success() (gas: 244024) @@ -34,10 +19,7 @@ BurnWithFromMintTokenPool_lockOrBurn:test_ChainNotAllowed_Revert() (gas: 28842) BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurnRevertNotHealthy_Revert() (gas: 55271) BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurn_Success() (gas: 244050) BurnWithFromMintTokenPool_lockOrBurn:test_Setup_Success() (gas: 24168) -BurnWithFromMintTokenPool_releaseOrMint:test_Setup_Success() (gas: 24547) -BurnWithFromMintTokenPool_releaseOrMint:test_releaseOrMint_NegativeMintAmount_reverts() (gas: 93840) -BurnWithFromMintTokenPool_releaseOrMint:test_releaseOrMint_Success() (gas: 93423) -CCIPClientExample_sanity:test_ImmutableExamples_Success() (gas: 2052431) +CCIPClientExample_sanity:test_ImmutableExamples_Success() (gas: 2108185) CCIPHome__validateConfig:test__validateConfigLessTransmittersThanSigners_Success() (gas: 335794) CCIPHome__validateConfig:test__validateConfigSmallerFChain_Success() (gas: 468721) CCIPHome__validateConfig:test__validateConfig_ABIEncodedAddress_OfframpAddressCannotBeZero_Reverts() (gas: 290481) @@ -84,220 +66,9 @@ CCIPHome_revokeCandidate:test_revokeCandidate_success() (gas: 30676) CCIPHome_setCandidate:test_setCandidate_CanOnlySelfCall_reverts() (gas: 19051) CCIPHome_setCandidate:test_setCandidate_ConfigDigestMismatch_reverts() (gas: 1388623) CCIPHome_setCandidate:test_setCandidate_success() (gas: 1357884) -CommitStore_constructor:test_Constructor_Success() (gas: 2855567) -CommitStore_isUnpausedAndRMNHealthy:test_RMN_Success() (gas: 73954) -CommitStore_report:test_InvalidIntervalMinLargerThanMax_Revert() (gas: 28739) -CommitStore_report:test_InvalidInterval_Revert() (gas: 28679) -CommitStore_report:test_InvalidRootRevert() (gas: 27912) -CommitStore_report:test_OnlyGasPriceUpdates_Success() (gas: 53448) -CommitStore_report:test_OnlyPriceUpdateStaleReport_Revert() (gas: 59286) -CommitStore_report:test_OnlyTokenPriceUpdates_Success() (gas: 53446) -CommitStore_report:test_Paused_Revert() (gas: 21319) -CommitStore_report:test_ReportAndPriceUpdate_Success() (gas: 84485) -CommitStore_report:test_ReportOnlyRootSuccess_gas() (gas: 56342) -CommitStore_report:test_RootAlreadyCommitted_Revert() (gas: 64077) -CommitStore_report:test_StaleReportWithRoot_Success() (gas: 117309) -CommitStore_report:test_Unhealthy_Revert() (gas: 44823) -CommitStore_report:test_ValidPriceUpdateThenStaleReportWithRoot_Success() (gas: 98929) -CommitStore_report:test_ZeroEpochAndRound_Revert() (gas: 27707) -CommitStore_resetUnblessedRoots:test_OnlyOwner_Revert() (gas: 11376) -CommitStore_resetUnblessedRoots:test_ResetUnblessedRoots_Success() (gas: 144186) -CommitStore_setDynamicConfig:test_InvalidCommitStoreConfig_Revert() (gas: 37314) -CommitStore_setDynamicConfig:test_OnlyOwner_Revert() (gas: 37483) -CommitStore_setDynamicConfig:test_PriceEpochCleared_Success() (gas: 129329) -CommitStore_setLatestPriceEpochAndRound:test_OnlyOwner_Revert() (gas: 11099) -CommitStore_setLatestPriceEpochAndRound:test_SetLatestPriceEpochAndRound_Success() (gas: 20690) -CommitStore_setMinSeqNr:test_OnlyOwner_Revert() (gas: 11098) -CommitStore_verify:test_Blessed_Success() (gas: 96581) -CommitStore_verify:test_NotBlessed_Success() (gas: 61473) -CommitStore_verify:test_Paused_Revert() (gas: 18568) -CommitStore_verify:test_TooManyLeaves_Revert() (gas: 36848) -DefensiveExampleTest:test_HappyPath_Success() (gas: 200200) -DefensiveExampleTest:test_Recovery() (gas: 424476) -E2E:test_E2E_3MessagesSuccess_gas() (gas: 1106985) -EVM2EVMOffRamp__releaseOrMintToken:test__releaseOrMintToken_NotACompatiblePool_Revert() (gas: 38322) -EVM2EVMOffRamp__releaseOrMintToken:test__releaseOrMintToken_Success() (gas: 104438) -EVM2EVMOffRamp__releaseOrMintToken:test__releaseOrMintToken_TokenHandlingError_transfer_Revert() (gas: 86026) -EVM2EVMOffRamp__releaseOrMintToken:test_releaseOrMintToken_InvalidDataLength_Revert() (gas: 37365) -EVM2EVMOffRamp__releaseOrMintToken:test_releaseOrMintToken_ReleaseOrMintBalanceMismatch_Revert() (gas: 95013) -EVM2EVMOffRamp__releaseOrMintToken:test_releaseOrMintToken_TokenHandlingError_BalanceOf_Revert() (gas: 40341) -EVM2EVMOffRamp__releaseOrMintToken:test_releaseOrMintToken_skip_ReleaseOrMintBalanceMismatch_if_pool_Revert() (gas: 87189) -EVM2EVMOffRamp__releaseOrMintTokens:test_OverValueWithARLOff_Success() (gas: 381594) -EVM2EVMOffRamp__releaseOrMintTokens:test_PriceNotFoundForToken_Reverts() (gas: 140568) -EVM2EVMOffRamp__releaseOrMintTokens:test_RateLimitErrors_Reverts() (gas: 798833) -EVM2EVMOffRamp__releaseOrMintTokens:test_TokenHandlingError_Reverts() (gas: 178400) -EVM2EVMOffRamp__releaseOrMintTokens:test__releaseOrMintTokens_NotACompatiblePool_Reverts() (gas: 29681) -EVM2EVMOffRamp__releaseOrMintTokens:test_releaseOrMintTokens_InvalidDataLengthReturnData_Revert() (gas: 67146) -EVM2EVMOffRamp__releaseOrMintTokens:test_releaseOrMintTokens_InvalidEVMAddress_Revert() (gas: 43605) -EVM2EVMOffRamp__releaseOrMintTokens:test_releaseOrMintTokens_Success() (gas: 208068) -EVM2EVMOffRamp__releaseOrMintTokens:test_releaseOrMintTokens_destDenominatedDecimals_Success() (gas: 219365) -EVM2EVMOffRamp__report:test_Report_Success() (gas: 127774) -EVM2EVMOffRamp__trialExecute:test_RateLimitError_Success() (gas: 237406) -EVM2EVMOffRamp__trialExecute:test_TokenHandlingErrorIsCaught_Success() (gas: 246039) -EVM2EVMOffRamp__trialExecute:test_TokenPoolIsNotAContract_Success() (gas: 329283) -EVM2EVMOffRamp__trialExecute:test_trialExecute_Success() (gas: 310166) -EVM2EVMOffRamp_ccipReceive:test_Reverts() (gas: 17048) -EVM2EVMOffRamp_constructor:test_CommitStoreAlreadyInUse_Revert() (gas: 153120) -EVM2EVMOffRamp_constructor:test_Constructor_Success() (gas: 5212732) -EVM2EVMOffRamp_constructor:test_ZeroOnRampAddress_Revert() (gas: 143845) -EVM2EVMOffRamp_execute:test_EmptyReport_Revert() (gas: 21507) -EVM2EVMOffRamp_execute:test_InvalidMessageId_Revert() (gas: 36936) -EVM2EVMOffRamp_execute:test_InvalidSourceChain_Revert() (gas: 52324) -EVM2EVMOffRamp_execute:test_InvalidSourcePoolAddress_Success() (gas: 473387) -EVM2EVMOffRamp_execute:test_ManualExecutionNotYetEnabled_Revert() (gas: 48346) -EVM2EVMOffRamp_execute:test_MessageTooLarge_Revert() (gas: 153019) -EVM2EVMOffRamp_execute:test_Paused_Revert() (gas: 103946) -EVM2EVMOffRamp_execute:test_ReceiverError_Success() (gas: 165358) -EVM2EVMOffRamp_execute:test_RetryFailedMessageWithoutManualExecution_Success() (gas: 180107) -EVM2EVMOffRamp_execute:test_RootNotCommitted_Revert() (gas: 43157) -EVM2EVMOffRamp_execute:test_SingleMessageNoTokensUnordered_Success() (gas: 160119) -EVM2EVMOffRamp_execute:test_SingleMessageNoTokens_Success() (gas: 175497) -EVM2EVMOffRamp_execute:test_SingleMessageToNonCCIPReceiver_Success() (gas: 237901) -EVM2EVMOffRamp_execute:test_SingleMessagesNoTokensSuccess_gas() (gas: 115048) -EVM2EVMOffRamp_execute:test_SkippedIncorrectNonceStillExecutes_Success() (gas: 406606) -EVM2EVMOffRamp_execute:test_SkippedIncorrectNonce_Success() (gas: 54774) -EVM2EVMOffRamp_execute:test_StrictUntouchedToSuccess_Success() (gas: 132556) -EVM2EVMOffRamp_execute:test_TokenDataMismatch_Revert() (gas: 52786) -EVM2EVMOffRamp_execute:test_TwoMessagesWithTokensAndGE_Success() (gas: 564471) -EVM2EVMOffRamp_execute:test_TwoMessagesWithTokensSuccess_gas() (gas: 494719) -EVM2EVMOffRamp_execute:test_UnexpectedTokenData_Revert() (gas: 35887) -EVM2EVMOffRamp_execute:test_Unhealthy_Revert() (gas: 546333) -EVM2EVMOffRamp_execute:test_UnsupportedNumberOfTokens_Revert() (gas: 65298) -EVM2EVMOffRamp_execute:test__execute_SkippedAlreadyExecutedMessageUnordered_Success() (gas: 124107) -EVM2EVMOffRamp_execute:test__execute_SkippedAlreadyExecutedMessage_Success() (gas: 144365) -EVM2EVMOffRamp_execute:test_execute_RouterYULCall_Success() (gas: 394187) -EVM2EVMOffRamp_executeSingleMessage:test_MessageSender_Revert() (gas: 18685) -EVM2EVMOffRamp_executeSingleMessage:test_NonContractWithTokens_Success() (gas: 275257) -EVM2EVMOffRamp_executeSingleMessage:test_NonContract_Success() (gas: 18815) -EVM2EVMOffRamp_executeSingleMessage:test_TokenHandlingError_Revert() (gas: 223182) -EVM2EVMOffRamp_executeSingleMessage:test_ZeroGasDONExecution_Revert() (gas: 48391) -EVM2EVMOffRamp_executeSingleMessage:test_executeSingleMessage_NoTokens_Success() (gas: 47823) -EVM2EVMOffRamp_executeSingleMessage:test_executeSingleMessage_WithTokens_Success() (gas: 311554) -EVM2EVMOffRamp_executeSingleMessage:test_executeSingleMessage_ZeroGasZeroData_Success() (gas: 70839) -EVM2EVMOffRamp_execute_upgrade:test_V2NonceNewSenderStartsAtZero_Success() (gas: 232136) -EVM2EVMOffRamp_execute_upgrade:test_V2NonceStartsAtV1Nonce_Success() (gas: 281170) -EVM2EVMOffRamp_execute_upgrade:test_V2OffRampNonceSkipsIfMsgInFlight_Success() (gas: 262488) -EVM2EVMOffRamp_execute_upgrade:test_V2SenderNoncesReadsPreviousRamp_Success() (gas: 230645) -EVM2EVMOffRamp_execute_upgrade:test_V2_Success() (gas: 132092) -EVM2EVMOffRamp_getAllRateLimitTokens:test_GetAllRateLimitTokens_Success() (gas: 38626) -EVM2EVMOffRamp_getExecutionState:test_FillExecutionState_Success() (gas: 3397486) -EVM2EVMOffRamp_getExecutionState:test_GetExecutionState_Success() (gas: 84833) -EVM2EVMOffRamp_manuallyExecute:test_ManualExecFailedTx_Revert() (gas: 188280) -EVM2EVMOffRamp_manuallyExecute:test_ManualExecForkedChain_Revert() (gas: 27574) -EVM2EVMOffRamp_manuallyExecute:test_ManualExecGasLimitMismatch_Revert() (gas: 46457) -EVM2EVMOffRamp_manuallyExecute:test_ManualExecInvalidGasLimit_Revert() (gas: 27948) -EVM2EVMOffRamp_manuallyExecute:test_ManualExecWithMultipleMessagesAndSourceTokens_Success() (gas: 531330) -EVM2EVMOffRamp_manuallyExecute:test_ManualExecWithSourceTokens_Success() (gas: 344463) -EVM2EVMOffRamp_manuallyExecute:test_ManualExec_Success() (gas: 189760) -EVM2EVMOffRamp_manuallyExecute:test_ReentrancyManualExecuteFails_Success() (gas: 2195128) -EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_DestinationGasAmountCountMismatch_Revert() (gas: 362054) -EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_DoesNotRevertIfUntouched_Success() (gas: 145457) -EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_InvalidTokenGasOverride_Revert() (gas: 365283) -EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_LowGasLimitManualExec_Success() (gas: 450711) -EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_WithGasOverride_Success() (gas: 192223) -EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_WithInvalidReceiverExecutionGasOverride_Revert() (gas: 155387) -EVM2EVMOffRamp_manuallyExecute:test_manuallyExecute_WithInvalidSourceTokenDataCount_Revert() (gas: 60494) -EVM2EVMOffRamp_metadataHash:test_MetadataHash_Success() (gas: 8895) -EVM2EVMOffRamp_setDynamicConfig:test_NonOwner_Revert() (gas: 40357) -EVM2EVMOffRamp_setDynamicConfig:test_RouterZeroAddress_Revert() (gas: 38419) -EVM2EVMOffRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 142469) -EVM2EVMOffRamp_updateRateLimitTokens:test_updateRateLimitTokens_AddsAndRemoves_Success() (gas: 162818) -EVM2EVMOffRamp_updateRateLimitTokens:test_updateRateLimitTokens_NonOwner_Revert() (gas: 16936) -EVM2EVMOffRamp_updateRateLimitTokens:test_updateRateLimitTokens_Success() (gas: 197985) -EVM2EVMOnRamp_constructor:test_Constructor_Success() (gas: 5056698) -EVM2EVMOnRamp_forwardFromRouter:test_CannotSendZeroTokens_Revert() (gas: 36063) -EVM2EVMOnRamp_forwardFromRouter:test_EnforceOutOfOrder_Revert() (gas: 99010) -EVM2EVMOnRamp_forwardFromRouter:test_ForwardFromRouterExtraArgsV2AllowOutOfOrderTrue_Success() (gas: 114925) -EVM2EVMOnRamp_forwardFromRouter:test_ForwardFromRouterExtraArgsV2_Success() (gas: 114967) -EVM2EVMOnRamp_forwardFromRouter:test_ForwardFromRouterSuccessCustomExtraArgs() (gas: 130991) -EVM2EVMOnRamp_forwardFromRouter:test_ForwardFromRouterSuccessLegacyExtraArgs() (gas: 139431) -EVM2EVMOnRamp_forwardFromRouter:test_ForwardFromRouter_Success() (gas: 130607) -EVM2EVMOnRamp_forwardFromRouter:test_InvalidAddressEncodePacked_Revert() (gas: 38647) -EVM2EVMOnRamp_forwardFromRouter:test_InvalidAddress_Revert() (gas: 38830) -EVM2EVMOnRamp_forwardFromRouter:test_InvalidChainSelector_Revert() (gas: 25726) -EVM2EVMOnRamp_forwardFromRouter:test_InvalidExtraArgsTag_Revert() (gas: 25545) -EVM2EVMOnRamp_forwardFromRouter:test_MaxCapacityExceeded_Revert() (gas: 84266) -EVM2EVMOnRamp_forwardFromRouter:test_MaxFeeBalanceReached_Revert() (gas: 36847) -EVM2EVMOnRamp_forwardFromRouter:test_MessageGasLimitTooHigh_Revert() (gas: 29327) -EVM2EVMOnRamp_forwardFromRouter:test_MessageTooLarge_Revert() (gas: 107850) -EVM2EVMOnRamp_forwardFromRouter:test_OriginalSender_Revert() (gas: 22823) -EVM2EVMOnRamp_forwardFromRouter:test_OverValueWithARLOff_Success() (gas: 226568) -EVM2EVMOnRamp_forwardFromRouter:test_Paused_Revert() (gas: 53432) -EVM2EVMOnRamp_forwardFromRouter:test_Permissions_Revert() (gas: 25757) -EVM2EVMOnRamp_forwardFromRouter:test_PriceNotFoundForToken_Revert() (gas: 57722) -EVM2EVMOnRamp_forwardFromRouter:test_ShouldIncrementNonceOnlyOnOrdered_Success() (gas: 182247) -EVM2EVMOnRamp_forwardFromRouter:test_ShouldIncrementSeqNumAndNonce_Success() (gas: 180718) -EVM2EVMOnRamp_forwardFromRouter:test_ShouldStoreNonLinkFees() (gas: 133236) -EVM2EVMOnRamp_forwardFromRouter:test_SourceTokenDataTooLarge_Revert() (gas: 3573653) -EVM2EVMOnRamp_forwardFromRouter:test_TooManyTokens_Revert() (gas: 30472) -EVM2EVMOnRamp_forwardFromRouter:test_Unhealthy_Revert() (gas: 43480) -EVM2EVMOnRamp_forwardFromRouter:test_UnsupportedToken_Revert() (gas: 110111) -EVM2EVMOnRamp_forwardFromRouter:test_ZeroAddressReceiver_Revert() (gas: 316020) -EVM2EVMOnRamp_forwardFromRouter:test_forwardFromRouter_ShouldStoreLinkFees_Success() (gas: 113033) -EVM2EVMOnRamp_forwardFromRouter:test_forwardFromRouter_UnsupportedToken_Revert() (gas: 72824) -EVM2EVMOnRamp_forwardFromRouter:test_forwardFromRouter_correctSourceTokenData_Success() (gas: 714726) -EVM2EVMOnRamp_forwardFromRouter_upgrade:test_V2NonceNewSenderStartsAtZero_Success() (gas: 148808) -EVM2EVMOnRamp_forwardFromRouter_upgrade:test_V2NonceStartsAtV1Nonce_Success() (gas: 192679) -EVM2EVMOnRamp_forwardFromRouter_upgrade:test_V2SenderNoncesReadsPreviousRamp_Success() (gas: 123243) -EVM2EVMOnRamp_forwardFromRouter_upgrade:test_V2_Success() (gas: 96028) -EVM2EVMOnRamp_getDataAvailabilityCost:test_EmptyMessageCalculatesDataAvailabilityCost_Success() (gas: 20598) -EVM2EVMOnRamp_getDataAvailabilityCost:test_SimpleMessageCalculatesDataAvailabilityCost_Success() (gas: 20966) -EVM2EVMOnRamp_getFee:test_EmptyMessage_Success() (gas: 74894) -EVM2EVMOnRamp_getFee:test_GetFeeOfZeroForTokenMessage_Success() (gas: 80393) -EVM2EVMOnRamp_getFee:test_HighGasMessage_Success() (gas: 230742) -EVM2EVMOnRamp_getFee:test_MessageGasLimitTooHigh_Revert() (gas: 16943) -EVM2EVMOnRamp_getFee:test_MessageTooLarge_Revert() (gas: 95505) -EVM2EVMOnRamp_getFee:test_MessageWithDataAndTokenTransfer_Success() (gas: 154010) -EVM2EVMOnRamp_getFee:test_NotAFeeToken_Revert() (gas: 24323) -EVM2EVMOnRamp_getFee:test_SingleTokenMessage_Success() (gas: 114740) -EVM2EVMOnRamp_getFee:test_TooManyTokens_Revert() (gas: 20142) -EVM2EVMOnRamp_getFee:test_ZeroDataAvailabilityMultiplier_Success() (gas: 63070) -EVM2EVMOnRamp_getSupportedTokens:test_GetSupportedTokens_Revert() (gas: 10532) -EVM2EVMOnRamp_getTokenPool:test_GetTokenPool_Success() (gas: 35297) -EVM2EVMOnRamp_getTokenTransferCost:test_CustomTokenBpsFee_Success() (gas: 43218) -EVM2EVMOnRamp_getTokenTransferCost:test_FeeTokenBpsFee_Success() (gas: 33280) -EVM2EVMOnRamp_getTokenTransferCost:test_LargeTokenTransferChargesMaxFeeAndGas_Success() (gas: 28551) -EVM2EVMOnRamp_getTokenTransferCost:test_MixedTokenTransferFee_Success() (gas: 122690) -EVM2EVMOnRamp_getTokenTransferCost:test_NoTokenTransferChargesZeroFee_Success() (gas: 15403) -EVM2EVMOnRamp_getTokenTransferCost:test_SmallTokenTransferChargesMinFeeAndGas_Success() (gas: 28359) -EVM2EVMOnRamp_getTokenTransferCost:test_UnsupportedToken_Revert() (gas: 21353) -EVM2EVMOnRamp_getTokenTransferCost:test_ZeroAmountTokenTransferChargesMinFeeAndGas_Success() (gas: 28382) -EVM2EVMOnRamp_getTokenTransferCost:test_ZeroFeeConfigChargesMinFee_Success() (gas: 38899) -EVM2EVMOnRamp_getTokenTransferCost:test__getTokenTransferCost_selfServeUsesDefaults_Success() (gas: 29674) -EVM2EVMOnRamp_linkAvailableForPayment:test_InsufficientLinkBalance_Success() (gas: 32756) -EVM2EVMOnRamp_linkAvailableForPayment:test_LinkAvailableForPayment_Success() (gas: 135247) -EVM2EVMOnRamp_payNops:test_AdminPayNops_Success() (gas: 143660) -EVM2EVMOnRamp_payNops:test_InsufficientBalance_Revert() (gas: 29196) -EVM2EVMOnRamp_payNops:test_NoFeesToPay_Revert() (gas: 127718) -EVM2EVMOnRamp_payNops:test_NoNopsToPay_Revert() (gas: 133580) -EVM2EVMOnRamp_payNops:test_NopPayNops_Success() (gas: 146947) -EVM2EVMOnRamp_payNops:test_OwnerPayNops_Success() (gas: 141522) -EVM2EVMOnRamp_payNops:test_PayNopsSuccessAfterSetNops() (gas: 298719) -EVM2EVMOnRamp_payNops:test_WrongPermissions_Revert() (gas: 15378) -EVM2EVMOnRamp_setDynamicConfig:test_SetConfigInvalidConfig_Revert() (gas: 42524) -EVM2EVMOnRamp_setDynamicConfig:test_SetConfigOnlyOwner_Revert() (gas: 21426) -EVM2EVMOnRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 54301) -EVM2EVMOnRamp_setFeeTokenConfig:test_OnlyCallableByOwnerOrAdmin_Revert() (gas: 13530) -EVM2EVMOnRamp_setFeeTokenConfig:test_SetFeeTokenConfigByAdmin_Success() (gas: 16497) -EVM2EVMOnRamp_setFeeTokenConfig:test_SetFeeTokenConfig_Success() (gas: 14036) -EVM2EVMOnRamp_setNops:test_AdminCanSetNops_Success() (gas: 61872) -EVM2EVMOnRamp_setNops:test_IncludesPayment_Success() (gas: 470835) -EVM2EVMOnRamp_setNops:test_LinkTokenCannotBeNop_Revert() (gas: 57370) -EVM2EVMOnRamp_setNops:test_NonOwnerOrAdmin_Revert() (gas: 14779) -EVM2EVMOnRamp_setNops:test_NotEnoughFundsForPayout_Revert() (gas: 85200) -EVM2EVMOnRamp_setNops:test_SetNopsRemovesOldNopsCompletely_Success() (gas: 60868) -EVM2EVMOnRamp_setNops:test_SetNops_Success() (gas: 174097) -EVM2EVMOnRamp_setNops:test_TooManyNops_Revert() (gas: 193503) -EVM2EVMOnRamp_setNops:test_ZeroAddressCannotBeNop_Revert() (gas: 53711) -EVM2EVMOnRamp_setTokenTransferFeeConfig:test__setTokenTransferFeeConfig_InvalidDestBytesOverhead_Revert() (gas: 14616) -EVM2EVMOnRamp_setTokenTransferFeeConfig:test__setTokenTransferFeeConfig_OnlyCallableByOwnerOrAdmin_Revert() (gas: 14427) -EVM2EVMOnRamp_setTokenTransferFeeConfig:test__setTokenTransferFeeConfig_Success() (gas: 85487) -EVM2EVMOnRamp_setTokenTransferFeeConfig:test__setTokenTransferFeeConfig_byAdmin_Success() (gas: 17468) -EVM2EVMOnRamp_withdrawNonLinkFees:test_LinkBalanceNotSettled_Revert() (gas: 83617) -EVM2EVMOnRamp_withdrawNonLinkFees:test_NonOwnerOrAdmin_Revert() (gas: 15353) -EVM2EVMOnRamp_withdrawNonLinkFees:test_SettlingBalance_Success() (gas: 272851) -EVM2EVMOnRamp_withdrawNonLinkFees:test_WithdrawNonLinkFees_Success() (gas: 53566) -EVM2EVMOnRamp_withdrawNonLinkFees:test_WithdrawToZeroAddress_Revert() (gas: 12875) +DefensiveExampleTest:test_HappyPath_Success() (gas: 200008) +DefensiveExampleTest:test_Recovery() (gas: 424284) +E2E:test_E2E_3MessagesMMultiOffRampSuccess_gas() (gas: 1501725) EtherSenderReceiverTest_ccipReceive:test_ccipReceive_fallbackToWethTransfer() (gas: 96907) EtherSenderReceiverTest_ccipReceive:test_ccipReceive_happyPath() (gas: 49775) EtherSenderReceiverTest_ccipReceive:test_ccipReceive_wrongToken() (gas: 17435) @@ -450,14 +221,6 @@ HybridUSDCTokenPoolTests:test_MintOrRelease_incomingMessageWithPrimaryMechanism( HybridUSDCTokenPoolTests:test_ReleaseOrMint_WhileMigrationPause_Revert() (gas: 111528) HybridUSDCTokenPoolTests:test_cannotTransferLiquidityDuringPendingMigration_Revert() (gas: 160845) HybridUSDCTokenPoolTests:test_transferLiquidity_Success() (gas: 165904) -LockReleaseTokenPoolAndProxy_setRebalancer:test_SetRebalancer_Revert() (gas: 10989) -LockReleaseTokenPoolAndProxy_setRebalancer:test_SetRebalancer_Success() (gas: 18028) -LockReleaseTokenPoolPoolAndProxy_canAcceptLiquidity:test_CanAcceptLiquidity_Success() (gas: 3051552) -LockReleaseTokenPoolPoolAndProxy_provideLiquidity:test_LiquidityNotAccepted_Revert() (gas: 3047988) -LockReleaseTokenPoolPoolAndProxy_provideLiquidity:test_Unauthorized_Revert() (gas: 11489) -LockReleaseTokenPoolPoolAndProxy_supportsInterface:test_SupportsInterface_Success() (gas: 10196) -LockReleaseTokenPoolPoolAndProxy_withdrawalLiquidity:test_InsufficientLiquidity_Revert() (gas: 60187) -LockReleaseTokenPoolPoolAndProxy_withdrawalLiquidity:test_Unauthorized_Revert() (gas: 11464) LockReleaseTokenPool_canAcceptLiquidity:test_CanAcceptLiquidity_Success() (gas: 2836138) LockReleaseTokenPool_lockOrBurn:test_LockOrBurnWithAllowList_Revert() (gas: 30062) LockReleaseTokenPool_lockOrBurn:test_LockOrBurnWithAllowList_Success() (gas: 79943) @@ -564,23 +327,20 @@ MultiOCR3Base_transmit:test_UnAuthorizedTransmitter_Revert() (gas: 24234) MultiOCR3Base_transmit:test_UnauthorizedSigner_Revert() (gas: 61275) MultiOCR3Base_transmit:test_UnconfiguredPlugin_Revert() (gas: 39933) MultiOCR3Base_transmit:test_ZeroSignatures_Revert() (gas: 33049) -MultiOnRampTokenPoolReentrancy:test_OnRampTokenPoolReentrancy_Success() (gas: 233635) -MultiRampsE2E:test_E2E_3MessagesMMultiOffRampSuccess_gas() (gas: 1501725) NonceManager_NonceIncrementation:test_getIncrementedOutboundNonce_Success() (gas: 37934) NonceManager_NonceIncrementation:test_incrementInboundNonce_Skip() (gas: 23706) NonceManager_NonceIncrementation:test_incrementInboundNonce_Success() (gas: 38778) NonceManager_NonceIncrementation:test_incrementNoncesInboundAndOutbound_Success() (gas: 71901) -NonceManager_OffRampUpgrade:test_NoPrevOffRampForChain_Success() (gas: 262171) -NonceManager_OffRampUpgrade:test_UpgradedNonceNewSenderStartsAtZero_Success() (gas: 265848) -NonceManager_OffRampUpgrade:test_UpgradedNonceStartsAtV1Nonce_Success() (gas: 329848) -NonceManager_OffRampUpgrade:test_UpgradedOffRampNonceSkipsIfMsgInFlight_Success() (gas: 300818) -NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRampTransitive_Success() (gas: 249120) -NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRamp_Success() (gas: 237027) -NonceManager_OffRampUpgrade:test_Upgraded_Success() (gas: 153760) -NonceManager_OnRampUpgrade:test_UpgradeNonceNewSenderStartsAtZero_Success() (gas: 169036) -NonceManager_OnRampUpgrade:test_UpgradeNonceStartsAtV1Nonce_Success() (gas: 221191) -NonceManager_OnRampUpgrade:test_UpgradeSenderNoncesReadsPreviousRamp_Success() (gas: 126745) -NonceManager_OnRampUpgrade:test_Upgrade_Success() (gas: 107755) +NonceManager_OffRampUpgrade:test_NoPrevOffRampForChain_Success() (gas: 186557) +NonceManager_OffRampUpgrade:test_UpgradedNonceNewSenderStartsAtZero_Success() (gas: 189890) +NonceManager_OffRampUpgrade:test_UpgradedNonceStartsAtV1Nonce_Success() (gas: 253684) +NonceManager_OffRampUpgrade:test_UpgradedOffRampNonceSkipsIfMsgInFlight_Success() (gas: 221424) +NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRamp_Success() (gas: 60404) +NonceManager_OffRampUpgrade:test_Upgraded_Success() (gas: 153546) +NonceManager_OnRampUpgrade:test_UpgradeNonceNewSenderStartsAtZero_Success() (gas: 165998) +NonceManager_OnRampUpgrade:test_UpgradeNonceStartsAtV1Nonce_Success() (gas: 195569) +NonceManager_OnRampUpgrade:test_UpgradeSenderNoncesReadsPreviousRamp_Success() (gas: 139133) +NonceManager_OnRampUpgrade:test_Upgrade_Success() (gas: 105077) NonceManager_applyPreviousRampsUpdates:test_MultipleRampsUpdates() (gas: 123102) NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOffRamp_Revert() (gas: 43079) NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRampAndOffRamp_Revert() (gas: 64408) @@ -588,31 +348,6 @@ NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRamp_Revert( NonceManager_applyPreviousRampsUpdates:test_SingleRampUpdate() (gas: 66666) NonceManager_applyPreviousRampsUpdates:test_ZeroInput() (gas: 12070) NonceManager_typeAndVersion:test_typeAndVersion() (gas: 9705) -OCR2BaseNoChecks_setOCR2Config:test_FMustBePositive_Revert() (gas: 12210) -OCR2BaseNoChecks_setOCR2Config:test_RepeatAddress_Revert() (gas: 42431) -OCR2BaseNoChecks_setOCR2Config:test_SetConfigSuccess_gas() (gas: 84597) -OCR2BaseNoChecks_setOCR2Config:test_TooManyTransmitter_Revert() (gas: 38177) -OCR2BaseNoChecks_setOCR2Config:test_TransmitterCannotBeZeroAddress_Revert() (gas: 24308) -OCR2BaseNoChecks_transmit:test_ConfigDigestMismatch_Revert() (gas: 17499) -OCR2BaseNoChecks_transmit:test_ForkedChain_Revert() (gas: 26798) -OCR2BaseNoChecks_transmit:test_TransmitSuccess_gas() (gas: 27499) -OCR2BaseNoChecks_transmit:test_UnAuthorizedTransmitter_Revert() (gas: 21335) -OCR2Base_setOCR2Config:test_FMustBePositive_Revert() (gas: 12216) -OCR2Base_setOCR2Config:test_FTooHigh_Revert() (gas: 12372) -OCR2Base_setOCR2Config:test_OracleOutOfRegister_Revert() (gas: 14919) -OCR2Base_setOCR2Config:test_RepeatAddress_Revert() (gas: 45469) -OCR2Base_setOCR2Config:test_SetConfigSuccess_gas() (gas: 155220) -OCR2Base_setOCR2Config:test_SingerCannotBeZeroAddress_Revert() (gas: 24425) -OCR2Base_setOCR2Config:test_TooManySigners_Revert() (gas: 20535) -OCR2Base_setOCR2Config:test_TransmitterCannotBeZeroAddress_Revert() (gas: 47316) -OCR2Base_transmit:test_ConfigDigestMismatch_Revert() (gas: 19668) -OCR2Base_transmit:test_ForkedChain_Revert() (gas: 37749) -OCR2Base_transmit:test_NonUniqueSignature_Revert() (gas: 55360) -OCR2Base_transmit:test_SignatureOutOfRegistration_Revert() (gas: 20989) -OCR2Base_transmit:test_Transmit2SignersSuccess_gas() (gas: 51689) -OCR2Base_transmit:test_UnAuthorizedTransmitter_Revert() (gas: 23511) -OCR2Base_transmit:test_UnauthorizedSigner_Revert() (gas: 39707) -OCR2Base_transmit:test_WrongNumberOfSignatures_Revert() (gas: 20584) OffRamp_afterOC3ConfigSet:test_afterOCR3ConfigSet_SignatureVerificationDisabled_Revert() (gas: 5913989) OffRamp_applySourceChainConfigUpdates:test_AddMultipleChains_Success() (gas: 626106) OffRamp_applySourceChainConfigUpdates:test_AddNewChain_Success() (gas: 166490) @@ -747,7 +482,7 @@ OffRamp_trialExecute:test_RateLimitError_Success() (gas: 219377) OffRamp_trialExecute:test_TokenHandlingErrorIsCaught_Success() (gas: 227999) OffRamp_trialExecute:test_TokenPoolIsNotAContract_Success() (gas: 295396) OffRamp_trialExecute:test_trialExecute_Success() (gas: 277896) -OnRampTokenPoolReentrancy:test_OnRampTokenPoolReentrancy_Success() (gas: 390842) +OnRampTokenPoolReentrancy:test_OnRampTokenPoolReentrancy_Success() (gas: 250726) OnRamp_applyAllowListUpdates:test_applyAllowListUpdates_InvalidAllowListRequestDisabledAllowListWithAdds() (gas: 18018) OnRamp_applyAllowListUpdates:test_applyAllowListUpdates_Revert() (gas: 67797) OnRamp_applyAllowListUpdates:test_applyAllowListUpdates_Success() (gas: 325198) @@ -797,11 +532,11 @@ OnRamp_setDynamicConfig:test_setDynamicConfig_InvalidConfigOnlyOwner_Revert() (g OnRamp_setDynamicConfig:test_setDynamicConfig_InvalidConfigReentrancyGuardEnteredEqTrue_Revert() (gas: 13265) OnRamp_setDynamicConfig:test_setDynamicConfig_Success() (gas: 56347) OnRamp_withdrawFeeTokens:test_WithdrawFeeTokens_Success() (gas: 97302) -PingPong_ccipReceive:test_CcipReceive_Success() (gas: 151349) -PingPong_plumbing:test_OutOfOrderExecution_Success() (gas: 20310) -PingPong_plumbing:test_Pausing_Success() (gas: 17810) -PingPong_startPingPong:test_StartPingPong_With_OOO_Success() (gas: 162091) -PingPong_startPingPong:test_StartPingPong_With_Sequenced_Ordered_Success() (gas: 181509) +PingPong_ccipReceive:test_CcipReceive_Success() (gas: 172562) +PingPong_plumbing:test_OutOfOrderExecution_Success() (gas: 20218) +PingPong_plumbing:test_Pausing_Success() (gas: 17718) +PingPong_startPingPong:test_StartPingPong_With_OOO_Success() (gas: 153698) +PingPong_startPingPong:test_StartPingPong_With_Sequenced_Ordered_Success() (gas: 179335) RMNHome__validateStaticAndDynamicConfig:test_validateStaticAndDynamicConfig_DuplicateOffchainPublicKey_reverts() (gas: 18822) RMNHome__validateStaticAndDynamicConfig:test_validateStaticAndDynamicConfig_DuplicatePeerId_reverts() (gas: 18682) RMNHome__validateStaticAndDynamicConfig:test_validateStaticAndDynamicConfig_DuplicateSourceChain_reverts() (gas: 20371) @@ -846,62 +581,6 @@ RMNRemote_verify_withConfigSet:test_verify_ThresholdNotMet_reverts() (gas: 15300 RMNRemote_verify_withConfigSet:test_verify_UnexpectedSigner_reverts() (gas: 387667) RMNRemote_verify_withConfigSet:test_verify_minSignersIsZero_success() (gas: 184524) RMNRemote_verify_withConfigSet:test_verify_success() (gas: 68207) -RMN_constructor:test_Constructor_Success() (gas: 48994) -RMN_getRecordedCurseRelatedOps:test_OpsPostDeployment() (gas: 19732) -RMN_lazyVoteToCurseUpdate_Benchmark:test_VoteToCurseLazilyRetain3VotersUponConfigChange_gas() (gas: 152296) -RMN_ownerUnbless:test_Unbless_Success() (gas: 74936) -RMN_ownerUnvoteToCurse:test_CanBlessAndCurseAfterGlobalCurseIsLifted() (gas: 471829) -RMN_ownerUnvoteToCurse:test_IsIdempotent() (gas: 398492) -RMN_ownerUnvoteToCurse:test_NonOwner_Revert() (gas: 18723) -RMN_ownerUnvoteToCurse:test_OwnerUnvoteToCurseSuccess_gas() (gas: 358084) -RMN_ownerUnvoteToCurse:test_UnknownVoter_Revert() (gas: 33190) -RMN_ownerUnvoteToCurse_Benchmark:test_OwnerUnvoteToCurse_1Voter_LiftsCurse_gas() (gas: 262408) -RMN_permaBlessing:test_PermaBlessing() (gas: 202777) -RMN_setConfig:test_BlessVoterIsZeroAddress_Revert() (gas: 15500) -RMN_setConfig:test_EitherThresholdIsZero_Revert() (gas: 21107) -RMN_setConfig:test_NonOwner_Revert() (gas: 14725) -RMN_setConfig:test_RepeatedAddress_Revert() (gas: 18219) -RMN_setConfig:test_SetConfigSuccess_gas() (gas: 104154) -RMN_setConfig:test_TotalWeightsSmallerThanEachThreshold_Revert() (gas: 30185) -RMN_setConfig:test_VoteToBlessByEjectedVoter_Revert() (gas: 130461) -RMN_setConfig:test_VotersLengthIsZero_Revert() (gas: 12149) -RMN_setConfig:test_WeightIsZeroAddress_Revert() (gas: 15740) -RMN_setConfig_Benchmark_1:test_SetConfig_7Voters_gas() (gas: 659600) -RMN_setConfig_Benchmark_2:test_ResetConfig_7Voters_gas() (gas: 212652) -RMN_unvoteToCurse:test_InvalidCursesHash() (gas: 26430) -RMN_unvoteToCurse:test_OwnerSkips() (gas: 33831) -RMN_unvoteToCurse:test_OwnerSucceeds() (gas: 64005) -RMN_unvoteToCurse:test_UnauthorizedVoter() (gas: 47715) -RMN_unvoteToCurse:test_ValidCursesHash() (gas: 61145) -RMN_unvoteToCurse:test_VotersCantLiftCurseButOwnerCan() (gas: 629190) -RMN_voteToBless:test_Curse_Revert() (gas: 473408) -RMN_voteToBless:test_IsAlreadyBlessed_Revert() (gas: 115435) -RMN_voteToBless:test_RootSuccess() (gas: 558661) -RMN_voteToBless:test_SenderAlreadyVoted_Revert() (gas: 97234) -RMN_voteToBless:test_UnauthorizedVoter_Revert() (gas: 17126) -RMN_voteToBless_Benchmark:test_1RootSuccess_gas() (gas: 44718) -RMN_voteToBless_Benchmark:test_3RootSuccess_gas() (gas: 98694) -RMN_voteToBless_Benchmark:test_5RootSuccess_gas() (gas: 152608) -RMN_voteToBless_Blessed_Benchmark:test_1RootSuccessBecameBlessed_gas() (gas: 29682) -RMN_voteToBless_Blessed_Benchmark:test_1RootSuccess_gas() (gas: 27628) -RMN_voteToBless_Blessed_Benchmark:test_3RootSuccess_gas() (gas: 81626) -RMN_voteToBless_Blessed_Benchmark:test_5RootSuccess_gas() (gas: 135518) -RMN_voteToCurse:test_CurseOnlyWhenThresholdReached_Success() (gas: 1651170) -RMN_voteToCurse:test_EmptySubjects_Revert() (gas: 14061) -RMN_voteToCurse:test_EvenIfAlreadyCursed_Success() (gas: 535124) -RMN_voteToCurse:test_OwnerCanCurseAndUncurse() (gas: 400060) -RMN_voteToCurse:test_RepeatedSubject_Revert() (gas: 144405) -RMN_voteToCurse:test_ReusedCurseId_Revert() (gas: 146972) -RMN_voteToCurse:test_UnauthorizedVoter_Revert() (gas: 12666) -RMN_voteToCurse:test_VoteToCurse_NoCurse_Success() (gas: 187556) -RMN_voteToCurse:test_VoteToCurse_YesCurse_Success() (gas: 473079) -RMN_voteToCurse_2:test_VotesAreDroppedIfSubjectIsNotCursedDuringConfigChange() (gas: 371083) -RMN_voteToCurse_2:test_VotesAreRetainedIfSubjectIsCursedDuringConfigChange() (gas: 1154362) -RMN_voteToCurse_Benchmark_1:test_VoteToCurse_NewSubject_NewVoter_NoCurse_gas() (gas: 141118) -RMN_voteToCurse_Benchmark_1:test_VoteToCurse_NewSubject_NewVoter_YesCurse_gas() (gas: 165258) -RMN_voteToCurse_Benchmark_2:test_VoteToCurse_OldSubject_NewVoter_NoCurse_gas() (gas: 121437) -RMN_voteToCurse_Benchmark_2:test_VoteToCurse_OldSubject_OldVoter_NoCurse_gas() (gas: 98373) -RMN_voteToCurse_Benchmark_3:test_VoteToCurse_OldSubject_NewVoter_YesCurse_gas() (gas: 145784) RateLimiter_constructor:test_Constructor_Success() (gas: 19734) RateLimiter_consume:test_AggregateValueMaxCapacityExceeded_Revert() (gas: 16042) RateLimiter_consume:test_AggregateValueRateLimitReached_Revert() (gas: 22390) @@ -925,40 +604,36 @@ Router_applyRampUpdates:test_OffRampMismatch_Revert() (gas: 89366) Router_applyRampUpdates:test_OffRampUpdatesWithRouting() (gas: 10662612) Router_applyRampUpdates:test_OnRampDisable() (gas: 56007) Router_applyRampUpdates:test_OnlyOwner_Revert() (gas: 12356) -Router_ccipSend:test_CCIPSendLinkFeeNoTokenSuccess_gas() (gas: 114599) -Router_ccipSend:test_CCIPSendLinkFeeOneTokenSuccess_gas() (gas: 202430) -Router_ccipSend:test_CCIPSendNativeFeeNoTokenSuccess_gas() (gas: 126968) -Router_ccipSend:test_CCIPSendNativeFeeOneTokenSuccess_gas() (gas: 214801) -Router_ccipSend:test_FeeTokenAmountTooLow_Revert() (gas: 64520) -Router_ccipSend:test_InvalidMsgValue() (gas: 32155) -Router_ccipSend:test_NativeFeeTokenInsufficientValue() (gas: 67177) -Router_ccipSend:test_NativeFeeTokenOverpay_Success() (gas: 170385) -Router_ccipSend:test_NativeFeeTokenZeroValue() (gas: 54279) -Router_ccipSend:test_NativeFeeToken_Success() (gas: 168901) -Router_ccipSend:test_NonLinkFeeToken_Success() (gas: 239227) -Router_ccipSend:test_UnsupportedDestinationChain_Revert() (gas: 24854) -Router_ccipSend:test_WhenNotHealthy_Revert() (gas: 44811) -Router_ccipSend:test_WrappedNativeFeeToken_Success() (gas: 171189) -Router_ccipSend:test_ZeroFeeAndGasPrice_Success() (gas: 241701) -Router_constructor:test_Constructor_Success() (gas: 13128) +Router_ccipSend:test_CCIPSendLinkFeeNoTokenSuccess_gas() (gas: 131213) +Router_ccipSend:test_CCIPSendLinkFeeOneTokenSuccess_gas() (gas: 220867) +Router_ccipSend:test_FeeTokenAmountTooLow_Revert() (gas: 71620) +Router_ccipSend:test_InvalidMsgValue() (gas: 32267) +Router_ccipSend:test_NativeFeeTokenInsufficientValue() (gas: 69330) +Router_ccipSend:test_NativeFeeTokenOverpay_Success() (gas: 192902) +Router_ccipSend:test_NativeFeeTokenZeroValue() (gas: 61356) +Router_ccipSend:test_NativeFeeToken_Success() (gas: 191528) +Router_ccipSend:test_NonLinkFeeToken_Success() (gas: 226332) +Router_ccipSend:test_UnsupportedDestinationChain_Revert() (gas: 24900) +Router_ccipSend:test_WhenNotHealthy_Revert() (gas: 44924) +Router_ccipSend:test_WrappedNativeFeeToken_Success() (gas: 193750) +Router_ccipSend:test_ccipSend_nativeFeeNoTokenSuccess_gas() (gas: 140474) +Router_ccipSend:test_ccipSend_nativeFeeOneTokenSuccess_gas() (gas: 230063) +Router_constructor:test_Constructor_Success() (gas: 13120) Router_getArmProxy:test_getArmProxy() (gas: 10573) -Router_getFee:test_GetFeeSupportedChain_Success() (gas: 44673) -Router_getFee:test_UnsupportedDestinationChain_Revert() (gas: 17192) -Router_getSupportedTokens:test_GetSupportedTokens_Revert() (gas: 10532) -Router_recoverTokens:test_RecoverTokensInvalidRecipient_Revert() (gas: 11334) -Router_recoverTokens:test_RecoverTokensNoFunds_Revert() (gas: 20267) +Router_getFee:test_GetFeeSupportedChain_Success() (gas: 51683) +Router_getFee:test_UnsupportedDestinationChain_Revert() (gas: 17150) +Router_getSupportedTokens:test_GetSupportedTokens_Revert() (gas: 10474) +Router_recoverTokens:test_RecoverTokensInvalidRecipient_Revert() (gas: 11378) +Router_recoverTokens:test_RecoverTokensNoFunds_Revert() (gas: 20245) Router_recoverTokens:test_RecoverTokensNonOwner_Revert() (gas: 11171) Router_recoverTokens:test_RecoverTokensValueReceiver_Revert() (gas: 358049) -Router_recoverTokens:test_RecoverTokens_Success() (gas: 52480) -Router_routeMessage:test_AutoExec_Success() (gas: 42816) -Router_routeMessage:test_ExecutionEvent_Success() (gas: 158520) -Router_routeMessage:test_ManualExec_Success() (gas: 35546) -Router_routeMessage:test_OnlyOffRamp_Revert() (gas: 25224) -Router_routeMessage:test_WhenNotHealthy_Revert() (gas: 44799) +Router_recoverTokens:test_RecoverTokens_Success() (gas: 52452) +Router_routeMessage:test_routeMessage_AutoExec_Success() (gas: 43092) +Router_routeMessage:test_routeMessage_ExecutionEvent_Success() (gas: 159137) +Router_routeMessage:test_routeMessage_ManualExec_Success() (gas: 35685) +Router_routeMessage:test_routeMessage_OnlyOffRamp_Revert() (gas: 25404) +Router_routeMessage:test_routeMessage_WhenNotHealthy_Revert() (gas: 44925) Router_setWrappedNative:test_OnlyOwner_Revert() (gas: 10998) -SelfFundedPingPong_ccipReceive:test_FundingIfNotANop_Revert() (gas: 55660) -SelfFundedPingPong_ccipReceive:test_Funding_Success() (gas: 421258) -SelfFundedPingPong_setCountIncrBeforeFunding:test_setCountIncrBeforeFunding() (gas: 20181) TokenAdminRegistry_acceptAdminRole:test_acceptAdminRole_OnlyPendingAdministrator_Revert() (gas: 51163) TokenAdminRegistry_acceptAdminRole:test_acceptAdminRole_Success() (gas: 44004) TokenAdminRegistry_addRegistryModule:test_addRegistryModule_OnlyOwner_Revert() (gas: 12653) @@ -981,12 +656,6 @@ TokenAdminRegistry_setPool:test_setPool_Success() (gas: 36135) TokenAdminRegistry_setPool:test_setPool_ZeroAddressRemovesPool_Success() (gas: 30842) TokenAdminRegistry_transferAdminRole:test_transferAdminRole_OnlyAdministrator_Revert() (gas: 18103) TokenAdminRegistry_transferAdminRole:test_transferAdminRole_Success() (gas: 49438) -TokenPoolAndProxy:test_lockOrBurn_burnMint_Success() (gas: 5586499) -TokenPoolAndProxy:test_lockOrBurn_burnWithFromMint_Success() (gas: 5618769) -TokenPoolAndProxy:test_lockOrBurn_lockRelease_Success() (gas: 5793246) -TokenPoolAndProxy:test_setPreviousPool_Success() (gas: 3070731) -TokenPoolAndProxyMigration:test_tokenPoolMigration_Success_1_2() (gas: 6434801) -TokenPoolAndProxyMigration:test_tokenPoolMigration_Success_1_4() (gas: 6634934) TokenPoolWithAllowList_applyAllowListUpdates:test_AllowListNotEnabled_Revert() (gas: 1979943) TokenPoolWithAllowList_applyAllowListUpdates:test_OnlyOwner_Revert() (gas: 12113) TokenPoolWithAllowList_applyAllowListUpdates:test_SetAllowListSkipsZero_Success() (gas: 23476) @@ -1014,17 +683,6 @@ TokenPool_setChainRateLimiterConfig:test_OnlyOwnerOrRateLimitAdmin_Revert() (gas TokenPool_setRemotePool:test_setRemotePool_NonExistentChain_Reverts() (gas: 15671) TokenPool_setRemotePool:test_setRemotePool_OnlyOwner_Reverts() (gas: 13219) TokenPool_setRemotePool:test_setRemotePool_Success() (gas: 282125) -TokenProxy_ccipSend:test_CcipSendGasShouldBeZero_Revert() (gas: 17226) -TokenProxy_ccipSend:test_CcipSendInsufficientAllowance_Revert() (gas: 134605) -TokenProxy_ccipSend:test_CcipSendInvalidToken_Revert() (gas: 16000) -TokenProxy_ccipSend:test_CcipSendNative_Success() (gas: 244013) -TokenProxy_ccipSend:test_CcipSendNoDataAllowed_Revert() (gas: 16384) -TokenProxy_ccipSend:test_CcipSend_Success() (gas: 262651) -TokenProxy_constructor:test_Constructor() (gas: 13836) -TokenProxy_getFee:test_GetFeeGasShouldBeZero_Revert() (gas: 16899) -TokenProxy_getFee:test_GetFeeInvalidToken_Revert() (gas: 12706) -TokenProxy_getFee:test_GetFeeNoDataAllowed_Revert() (gas: 15885) -TokenProxy_getFee:test_GetFee_Success() (gas: 85240) USDCTokenPool__validateMessage:test_ValidateInvalidMessage_Revert() (gas: 25704) USDCTokenPool_lockOrBurn:test_CallerIsNotARampOnRouter_Revert() (gas: 35481) USDCTokenPool_lockOrBurn:test_LockOrBurnWithAllowList_Revert() (gas: 30235) diff --git a/contracts/gas-snapshots/keystone.gas-snapshot b/contracts/gas-snapshots/keystone.gas-snapshot index aae929de324..5fea0ef6cab 100644 --- a/contracts/gas-snapshots/keystone.gas-snapshot +++ b/contracts/gas-snapshots/keystone.gas-snapshot @@ -13,7 +13,7 @@ CapabilitiesRegistry_AddDONTest:test_RevertWhen_DuplicateNodeAdded() (gas: 11690 CapabilitiesRegistry_AddDONTest:test_RevertWhen_FaultToleranceIsZero() (gas: 43373) CapabilitiesRegistry_AddDONTest:test_RevertWhen_NodeAlreadyBelongsToWorkflowDON() (gas: 344037) CapabilitiesRegistry_AddDONTest:test_RevertWhen_NodeDoesNotSupportCapability() (gas: 180165) -CapabilitiesRegistry_AddDONTest_WhenMaliciousCapabilityConfigurationConfigured:test_RevertWhen_MaliciousCapabilitiesConfigContractTriesToRemoveCapabilitiesFromDONNodes() (gas: 340698) +CapabilitiesRegistry_AddDONTest_WhenMaliciousCapabilityConfigurationConfigured:test_RevertWhen_MaliciousCapabilitiesConfigContractTriesToRemoveCapabilitiesFromDONNodes() (gas: 340724) CapabilitiesRegistry_AddNodeOperatorsTest:test_AddNodeOperators() (gas: 184179) CapabilitiesRegistry_AddNodeOperatorsTest:test_RevertWhen_CalledByNonAdmin() (gas: 17646) CapabilitiesRegistry_AddNodeOperatorsTest:test_RevertWhen_NodeOperatorAdminAddressZero() (gas: 18542) @@ -56,7 +56,7 @@ CapabilitiesRegistry_RemoveNodesTest:test_RevertWhen_CalledByNonNodeOperatorAdmi CapabilitiesRegistry_RemoveNodesTest:test_RevertWhen_NodeDoesNotExist() (gas: 18373) CapabilitiesRegistry_RemoveNodesTest:test_RevertWhen_NodePartOfCapabilitiesDON() (gas: 385422) CapabilitiesRegistry_RemoveNodesTest:test_RevertWhen_P2PIDEmpty() (gas: 18363) -CapabilitiesRegistry_TypeAndVersionTest:test_TypeAndVersion() (gas: 9796) +CapabilitiesRegistry_TypeAndVersionTest:test_TypeAndVersion() (gas: 9813) CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_CalledByNonAdmin() (gas: 19323) CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_CapabilityDoesNotExist() (gas: 152958) CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_DONDoesNotExist() (gas: 17749) @@ -70,24 +70,25 @@ CapabilitiesRegistry_UpdateNodeOperatorTest:test_RevertWhen_NodeOperatorAdminIsZ CapabilitiesRegistry_UpdateNodeOperatorTest:test_RevertWhen_NodeOperatorDoesNotExist() (gas: 19746) CapabilitiesRegistry_UpdateNodeOperatorTest:test_RevertWhen_NodeOperatorIdAndParamLengthsMismatch() (gas: 15386) CapabilitiesRegistry_UpdateNodeOperatorTest:test_UpdatesNodeOperator() (gas: 36990) -CapabilitiesRegistry_UpdateNodesTest:test_CanUpdateParamsIfNodeSignerAddressNoLongerUsed() (gas: 258926) -CapabilitiesRegistry_UpdateNodesTest:test_OwnerCanUpdateNodes() (gas: 164628) -CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_AddingNodeWithInvalidCapability() (gas: 36000) +CapabilitiesRegistry_UpdateNodesTest:test_CanUpdateParamsIfNodeSignerAddressNoLongerUsed() (gas: 261420) +CapabilitiesRegistry_UpdateNodesTest:test_OwnerCanUpdateNodes() (gas: 164875) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_AddingNodeWithInvalidCapability() (gas: 36026) CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_CalledByAnotherNodeOperatorAdmin() (gas: 29326) CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_CalledByNonNodeOperatorAdminAndNonOwner() (gas: 29504) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_EncryptionPublicKeyEmpty() (gas: 29746) CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_NodeDoesNotExist() (gas: 29326) CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_NodeSignerAlreadyAssignedToAnotherNode() (gas: 31452) CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_P2PIDEmpty() (gas: 29292) -CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_RemovingCapabilityRequiredByCapabilityDON() (gas: 471131) -CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_RemovingCapabilityRequiredByWorkflowDON() (gas: 341412) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_RemovingCapabilityRequiredByCapabilityDON() (gas: 471157) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_RemovingCapabilityRequiredByWorkflowDON() (gas: 341503) CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_SignerAddressEmpty() (gas: 29184) -CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_UpdatingNodeWithoutCapabilities() (gas: 27713) -CapabilitiesRegistry_UpdateNodesTest:test_UpdatesNodeParams() (gas: 164682) -KeystoneForwarder_ReportTest:test_Report_ConfigVersion() (gas: 2006826) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_UpdatingNodeWithoutCapabilities() (gas: 27739) +CapabilitiesRegistry_UpdateNodesTest:test_UpdatesNodeParams() (gas: 164929) +KeystoneForwarder_ReportTest:test_Report_ConfigVersion() (gas: 2006834) KeystoneForwarder_ReportTest:test_Report_FailedDelieryWhenReportReceiverConsumesAllGas() (gas: 1004827) KeystoneForwarder_ReportTest:test_Report_FailedDeliveryWhenReceiverInterfaceNotSupported() (gas: 124952) KeystoneForwarder_ReportTest:test_Report_FailedDeliveryWhenReceiverNotContract() (gas: 127233) -KeystoneForwarder_ReportTest:test_Report_FailedDeliveryWhenReportReceiverConsumesAllGasAndInterfaceCheckUsesMax() (gas: 440602) +KeystoneForwarder_ReportTest:test_Report_FailedDeliveryWhenReportReceiverConsumesAllGasAndInterfaceCheckUsesMax() (gas: 440297) KeystoneForwarder_ReportTest:test_Report_SuccessfulDelivery() (gas: 362832) KeystoneForwarder_ReportTest:test_Report_SuccessfulRetryWithMoreGas() (gas: 510665) KeystoneForwarder_ReportTest:test_RevertWhen_AnySignatureIsInvalid() (gas: 86326) diff --git a/contracts/scripts/native_solc_compile_all_ccip b/contracts/scripts/native_solc_compile_all_ccip index 5c2fc6b00de..47a2758dfde 100755 --- a/contracts/scripts/native_solc_compile_all_ccip +++ b/contracts/scripts/native_solc_compile_all_ccip @@ -8,9 +8,7 @@ echo " └─────────────────────── SOLC_VERSION="0.8.24" OPTIMIZE_RUNS=26000 -OPTIMIZE_RUNS_OFFRAMP=18000 -OPTIMIZE_RUNS_ONRAMP=4100 -OPTIMIZE_RUNS_MULTI_OFFRAMP=800 +OPTIMIZE_RUNS_OFFRAMP=800 SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" @@ -28,18 +26,10 @@ compileContract () { local optimize_runs=$OPTIMIZE_RUNS case $1 in - "ccip/offRamp/EVM2EVMOffRamp.sol") + "ccip/offRamp/OffRamp.sol") echo "OffRamp uses $OPTIMIZE_RUNS_OFFRAMP optimizer runs." optimize_runs=$OPTIMIZE_RUNS_OFFRAMP ;; - "ccip/offRamp/OffRamp.sol") - echo "MultiOffRamp uses $OPTIMIZE_RUNS_MULTI_OFFRAMP optimizer runs." - optimize_runs=$OPTIMIZE_RUNS_MULTI_OFFRAMP - ;; - "ccip/onRamp/EVM2EVMOnRamp.sol") - echo "OnRamp uses $OPTIMIZE_RUNS_ONRAMP optimizer runs." - optimize_runs=$OPTIMIZE_RUNS_ONRAMP - ;; esac solc --overwrite --optimize --optimize-runs $optimize_runs --metadata-hash none \ @@ -53,21 +43,16 @@ compileContract () { # Solc produces and overwrites intermediary contracts. # Contracts should be ordered in reverse-import-complexity-order to minimize overwrite risks. -compileContract ccip/offRamp/EVM2EVMOffRamp.sol compileContract ccip/offRamp/OffRamp.sol -compileContract ccip/rmn/RMNRemote.sol -compileContract ccip/rmn/RMNHome.sol +compileContract ccip/FeeQuoter.sol +compileContract ccip/onRamp/OnRamp.sol compileContract ccip/applications/PingPongDemo.sol -compileContract ccip/applications/SelfFundedPingPong.sol compileContract ccip/applications/EtherSenderReceiver.sol -compileContract ccip/onRamp/OnRamp.sol -compileContract ccip/onRamp/EVM2EVMOnRamp.sol -compileContract ccip/CommitStore.sol +compileContract ccip/rmn/RMNRemote.sol +compileContract ccip/rmn/RMNHome.sol +compileContract ccip/rmn/ARMProxy.sol compileContract ccip/MultiAggregateRateLimiter.sol compileContract ccip/Router.sol -compileContract ccip/FeeQuoter.sol -compileContract ccip/RMN.sol -compileContract ccip/ARMProxy.sol compileContract ccip/tokenAdminRegistry/TokenAdminRegistry.sol compileContract ccip/tokenAdminRegistry/RegistryModuleOwnerCustom.sol compileContract ccip/capability/CCIPHome.sol @@ -80,18 +65,12 @@ compileContract ccip/pools/LockReleaseTokenPool.sol compileContract ccip/pools/BurnMintTokenPool.sol compileContract ccip/pools/BurnFromMintTokenPool.sol compileContract ccip/pools/BurnWithFromMintTokenPool.sol -compileContract ccip/pools/LockReleaseTokenPoolAndProxy.sol -compileContract ccip/pools/BurnMintTokenPoolAndProxy.sol -compileContract ccip/pools/BurnWithFromMintTokenPoolAndProxy.sol -compileContract ccip/pools/BurnWithFromMintRebasingTokenPool.sol compileContract ccip/pools/TokenPool.sol # Test helpers compileContract ccip/test/helpers/BurnMintERC677Helper.sol -compileContract ccip/test/helpers/CommitStoreHelper.sol compileContract ccip/test/helpers/MessageHasher.sol -compileContract ccip/test/helpers/CCIPReaderTester.sol compileContract ccip/test/helpers/USDCReaderTester.sol compileContract ccip/test/helpers/ReportCodec.sol compileContract ccip/test/helpers/receivers/MaybeRevertMessageReceiver.sol diff --git a/contracts/src/v0.8/ccip/AggregateRateLimiter.sol b/contracts/src/v0.8/ccip/AggregateRateLimiter.sol deleted file mode 100644 index 7401df2ed49..00000000000 --- a/contracts/src/v0.8/ccip/AggregateRateLimiter.sol +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {IPriceRegistry} from "./interfaces/IPriceRegistry.sol"; - -import {OwnerIsCreator} from "./../shared/access/OwnerIsCreator.sol"; -import {Client} from "./libraries/Client.sol"; -import {RateLimiter} from "./libraries/RateLimiter.sol"; -import {USDPriceWith18Decimals} from "./libraries/USDPriceWith18Decimals.sol"; - -/// @notice The aggregate rate limiter is a wrapper of the token bucket rate limiter -/// which permits rate limiting based on the aggregate value of a group of -/// token transfers, using a price registry to convert to a numeraire asset (e.g. USD). -contract AggregateRateLimiter is OwnerIsCreator { - using RateLimiter for RateLimiter.TokenBucket; - using USDPriceWith18Decimals for uint224; - - error PriceNotFoundForToken(address token); - - event AdminSet(address newAdmin); - - // The address of the token limit admin that has the same permissions as the owner. - address internal s_admin; - - // The token bucket object that contains the bucket state. - RateLimiter.TokenBucket private s_rateLimiter; - - /// @param config The RateLimiter.Config - constructor(RateLimiter.Config memory config) { - s_rateLimiter = RateLimiter.TokenBucket({ - rate: config.rate, - capacity: config.capacity, - tokens: config.capacity, - lastUpdated: uint32(block.timestamp), - isEnabled: config.isEnabled - }); - } - - /// @notice Consumes value from the rate limiter bucket based on the token value given. - function _rateLimitValue(uint256 value) internal { - s_rateLimiter._consume(value, address(0)); - } - - function _getTokenValue( - Client.EVMTokenAmount memory tokenAmount, - IPriceRegistry priceRegistry - ) internal view returns (uint256) { - // not fetching validated price, as price staleness is not important for value-based rate limiting - // we only need to verify the price is not 0 - uint224 pricePerToken = priceRegistry.getTokenPrice(tokenAmount.token).value; - if (pricePerToken == 0) revert PriceNotFoundForToken(tokenAmount.token); - return pricePerToken._calcUSDValueFromTokenAmount(tokenAmount.amount); - } - - /// @notice Gets the token bucket with its values for the block it was requested at. - /// @return The token bucket. - function currentRateLimiterState() external view returns (RateLimiter.TokenBucket memory) { - return s_rateLimiter._currentTokenBucketState(); - } - - /// @notice Sets the rate limited config. - /// @param config The new rate limiter config. - /// @dev should only be callable by the owner or token limit admin. - function setRateLimiterConfig(RateLimiter.Config memory config) external onlyAdminOrOwner { - s_rateLimiter._setTokenBucketConfig(config); - } - - // ================================================================ - // │ Access │ - // ================================================================ - - /// @notice Gets the token limit admin address. - /// @return the token limit admin address. - function getTokenLimitAdmin() external view returns (address) { - return s_admin; - } - - /// @notice Sets the token limit admin address. - /// @param newAdmin the address of the new admin. - /// @dev setting this to address(0) indicates there is no active admin. - function setAdmin(address newAdmin) external onlyAdminOrOwner { - s_admin = newAdmin; - emit AdminSet(newAdmin); - } - - /// @notice a modifier that allows the owner or the s_tokenLimitAdmin call the functions - /// it is applied to. - modifier onlyAdminOrOwner() { - if (msg.sender != owner() && msg.sender != s_admin) revert RateLimiter.OnlyCallableByAdminOrOwner(); - _; - } -} diff --git a/contracts/src/v0.8/ccip/CommitStore.sol b/contracts/src/v0.8/ccip/CommitStore.sol deleted file mode 100644 index 77c2864d4fe..00000000000 --- a/contracts/src/v0.8/ccip/CommitStore.sol +++ /dev/null @@ -1,314 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {ITypeAndVersion} from "../shared/interfaces/ITypeAndVersion.sol"; -import {ICommitStore} from "./interfaces/ICommitStore.sol"; -import {IPriceRegistry} from "./interfaces/IPriceRegistry.sol"; -import {IRMN} from "./interfaces/IRMN.sol"; - -import {Internal} from "./libraries/Internal.sol"; -import {MerkleMultiProof} from "./libraries/MerkleMultiProof.sol"; -import {OCR2Base} from "./ocr/OCR2Base.sol"; - -contract CommitStore is ICommitStore, ITypeAndVersion, OCR2Base { - error StaleReport(); - error PausedError(); - error InvalidInterval(Interval interval); - error InvalidRoot(); - error InvalidCommitStoreConfig(); - error CursedByRMN(); - error RootAlreadyCommitted(); - - event Paused(address account); - event Unpaused(address account); - /// @dev RMN depends on this event, if changing, please notify the RMN maintainers. - event ReportAccepted(CommitReport report); - event ConfigSet(StaticConfig staticConfig, DynamicConfig dynamicConfig); - event RootRemoved(bytes32 root); - event SequenceNumberSet(uint64 oldSeqNum, uint64 newSeqNum); - event LatestPriceEpochAndRoundSet(uint40 oldEpochAndRound, uint40 newEpochAndRound); - - /// @notice Static commit store config - /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers. - //solhint-disable gas-struct-packing - struct StaticConfig { - uint64 chainSelector; // ───────╮ Destination chainSelector - uint64 sourceChainSelector; // ─╯ Source chainSelector - address onRamp; // OnRamp address on the source chain - address rmnProxy; // RMN proxy address - } - - /// @notice Dynamic commit store config - struct DynamicConfig { - address priceRegistry; // Price registry address on the destination chain - } - - /// @notice a sequenceNumber interval - /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers. - struct Interval { - uint64 min; // ───╮ Minimum sequence number, inclusive - uint64 max; // ───╯ Maximum sequence number, inclusive - } - - /// @notice Report that is committed by the observing DON at the committing phase - /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers. - struct CommitReport { - Internal.PriceUpdates priceUpdates; - Interval interval; - bytes32 merkleRoot; - } - - // STATIC CONFIG - string public constant override typeAndVersion = "CommitStore 1.5.0"; - // Chain ID of this chain - uint64 internal immutable i_chainSelector; - // Chain ID of the source chain - uint64 internal immutable i_sourceChainSelector; - // The onRamp address on the source chain - address internal immutable i_onRamp; - // The address of the rmn proxy - address internal immutable i_rmnProxy; - - // DYNAMIC CONFIG - // The dynamic commitStore config - DynamicConfig internal s_dynamicConfig; - - // STATE - // The min sequence number expected for future messages - uint64 private s_minSeqNr = 1; - /// @dev The epoch and round of the last report - uint40 private s_latestPriceEpochAndRound; - /// @dev Whether this CommitStore is paused or not - bool private s_paused = false; - // merkleRoot => timestamp when received - mapping(bytes32 merkleRoot => uint256 timestamp) private s_roots; - - /// @param staticConfig Containing the static part of the commitStore config - /// @dev When instantiating OCR2Base we set UNIQUE_REPORTS to false, which means - /// that we do not require 2f+1 signatures on a report, only f+1 to save gas. 2f+1 is required - /// only if one must strictly ensure that for a given round there is only one valid report ever generated by - /// the DON. In our case additional valid reports (i.e. approved by >= f+1 oracles) are not a problem, as they will - /// will either be ignored (reverted as an invalid interval) or will be accepted as an additional valid price update. - constructor(StaticConfig memory staticConfig) OCR2Base(false) { - if ( - staticConfig.onRamp == address(0) || staticConfig.chainSelector == 0 || staticConfig.sourceChainSelector == 0 - || staticConfig.rmnProxy == address(0) - ) revert InvalidCommitStoreConfig(); - - i_chainSelector = staticConfig.chainSelector; - i_sourceChainSelector = staticConfig.sourceChainSelector; - i_onRamp = staticConfig.onRamp; - i_rmnProxy = staticConfig.rmnProxy; - } - - // ================================================================ - // │ Verification │ - // ================================================================ - - /// @notice Returns the next expected sequence number. - /// @return the next expected sequenceNumber. - function getExpectedNextSequenceNumber() external view returns (uint64) { - return s_minSeqNr; - } - - /// @notice Sets the minimum sequence number. - /// @param minSeqNr The new minimum sequence number. - function setMinSeqNr(uint64 minSeqNr) external onlyOwner { - uint64 oldSeqNum = s_minSeqNr; - - s_minSeqNr = minSeqNr; - - emit SequenceNumberSet(oldSeqNum, minSeqNr); - } - - /// @notice Returns the epoch and round of the last price update. - /// @return the latest price epoch and round. - function getLatestPriceEpochAndRound() external view returns (uint64) { - return s_latestPriceEpochAndRound; - } - - /// @notice Sets the latest epoch and round for price update. - /// @param latestPriceEpochAndRound The new epoch and round for prices. - function setLatestPriceEpochAndRound(uint40 latestPriceEpochAndRound) external onlyOwner { - uint40 oldEpochAndRound = s_latestPriceEpochAndRound; - - s_latestPriceEpochAndRound = latestPriceEpochAndRound; - - emit LatestPriceEpochAndRoundSet(oldEpochAndRound, latestPriceEpochAndRound); - } - - /// @notice Returns the timestamp of a potentially previously committed merkle root. - /// If the root was never committed 0 will be returned. - /// @param root The merkle root to check the commit status for. - /// @return the timestamp of the committed root or zero in the case that it was never - /// committed. - function getMerkleRoot(bytes32 root) external view returns (uint256) { - return s_roots[root]; - } - - /// @notice Returns if a root is blessed or not. - /// @param root The merkle root to check the blessing status for. - /// @return whether the root is blessed or not. - function isBlessed(bytes32 root) public view returns (bool) { - return IRMN(i_rmnProxy).isBlessed(IRMN.TaggedRoot({commitStore: address(this), root: root})); - } - - /// @notice Used by the owner in case an invalid sequence of roots has been - /// posted and needs to be removed. The interval in the report is trusted. - /// @param rootToReset The roots that will be reset. This function will only - /// reset roots that are not blessed. - function resetUnblessedRoots(bytes32[] calldata rootToReset) external onlyOwner { - for (uint256 i = 0; i < rootToReset.length; ++i) { - bytes32 root = rootToReset[i]; - if (!isBlessed(root)) { - delete s_roots[root]; - emit RootRemoved(root); - } - } - } - - /// @inheritdoc ICommitStore - function verify( - bytes32[] calldata hashedLeaves, - bytes32[] calldata proofs, - uint256 proofFlagBits - ) external view override whenNotPaused returns (uint256 timestamp) { - bytes32 root = MerkleMultiProof.merkleRoot(hashedLeaves, proofs, proofFlagBits); - // Only return non-zero if present and blessed. - if (!isBlessed(root)) { - return 0; - } - return s_roots[root]; - } - - /// @inheritdoc OCR2Base - /// @dev A commitReport can have two distinct parts (batched together to amortize the cost of checking sigs): - /// 1. Price updates - /// 2. A merkle root and sequence number interval - /// Both have their own, separate, staleness checks, with price updates using the epoch and round - /// number of the latest price update. The merkle root checks for staleness based on the seqNums. - /// They need to be separate because a price report for round t+2 might be included before a report - /// containing a merkle root for round t+1. This merkle root report for round t+1 is still valid - /// and should not be rejected. When a report with a stale root but valid price updates is submitted, - /// we are OK to revert to preserve the invariant that we always revert on invalid sequence number ranges. - /// If that happens, prices will be updates in later rounds. - function _report(bytes calldata encodedReport, uint40 epochAndRound) internal override whenNotPaused { - if (IRMN(i_rmnProxy).isCursed(bytes16(uint128(i_sourceChainSelector)))) revert CursedByRMN(); - - CommitReport memory report = abi.decode(encodedReport, (CommitReport)); - - // Check if the report contains price updates - if (report.priceUpdates.tokenPriceUpdates.length > 0 || report.priceUpdates.gasPriceUpdates.length > 0) { - // Check for price staleness based on the epoch and round - if (s_latestPriceEpochAndRound < epochAndRound) { - // If prices are not stale, update the latest epoch and round - s_latestPriceEpochAndRound = epochAndRound; - // And update the prices in the price registry - IPriceRegistry(s_dynamicConfig.priceRegistry).updatePrices(report.priceUpdates); - - // If there is no root, the report only contained fee updated and - // we return to not revert on the empty root check below. - if (report.merkleRoot == bytes32(0)) return; - } else { - // If prices are stale and the report doesn't contain a root, this report - // does not have any valid information and we revert. - // If it does contain a merkle root, continue to the root checking section. - if (report.merkleRoot == bytes32(0)) revert StaleReport(); - } - } - - // If we reached this section, the report should contain a valid root - if (s_minSeqNr != report.interval.min || report.interval.min > report.interval.max) { - revert InvalidInterval(report.interval); - } - - if (report.merkleRoot == bytes32(0)) revert InvalidRoot(); - // Disallow duplicate roots as that would reset the timestamp and - // delay potential manual execution. - if (s_roots[report.merkleRoot] != 0) revert RootAlreadyCommitted(); - - s_minSeqNr = report.interval.max + 1; - s_roots[report.merkleRoot] = block.timestamp; - emit ReportAccepted(report); - } - - // ================================================================ - // │ Config │ - // ================================================================ - - /// @notice Returns the static commit store config. - /// @dev RMN depends on this function, if changing, please notify the RMN maintainers. - /// @return the configuration. - function getStaticConfig() external view returns (StaticConfig memory) { - return StaticConfig({ - chainSelector: i_chainSelector, - sourceChainSelector: i_sourceChainSelector, - onRamp: i_onRamp, - rmnProxy: i_rmnProxy - }); - } - - /// @notice Returns the dynamic commit store config. - /// @return the configuration. - function getDynamicConfig() external view returns (DynamicConfig memory) { - return s_dynamicConfig; - } - - /// @notice Sets the dynamic config. This function is called during `setOCR2Config` flow - function _beforeSetConfig(bytes memory onchainConfig) internal override { - DynamicConfig memory dynamicConfig = abi.decode(onchainConfig, (DynamicConfig)); - - if (dynamicConfig.priceRegistry == address(0)) revert InvalidCommitStoreConfig(); - - s_dynamicConfig = dynamicConfig; - // When the OCR config changes, we reset the price epoch and round - // since epoch and rounds are scoped per config digest. - // Note that s_minSeqNr/roots do not need to be reset as the roots persist - // across reconfigurations and are de-duplicated separately. - s_latestPriceEpochAndRound = 0; - - emit ConfigSet( - StaticConfig({ - chainSelector: i_chainSelector, - sourceChainSelector: i_sourceChainSelector, - onRamp: i_onRamp, - rmnProxy: i_rmnProxy - }), - dynamicConfig - ); - } - - // ================================================================ - // │ Access and RMN │ - // ================================================================ - - /// @notice Single function to check the status of the commitStore. - function isUnpausedAndNotCursed() external view returns (bool) { - return !IRMN(i_rmnProxy).isCursed(bytes16(uint128(i_sourceChainSelector))) && !s_paused; - } - - /// @notice Modifier to make a function callable only when the contract is not paused. - modifier whenNotPaused() { - if (paused()) revert PausedError(); - _; - } - - /// @notice Returns true if the contract is paused, and false otherwise. - function paused() public view returns (bool) { - return s_paused; - } - - /// @notice Pause the contract - /// @dev only callable by the owner - function pause() external onlyOwner { - s_paused = true; - emit Paused(msg.sender); - } - - /// @notice Unpause the contract - /// @dev only callable by the owner - function unpause() external onlyOwner { - s_paused = false; - emit Unpaused(msg.sender); - } -} diff --git a/contracts/src/v0.8/ccip/FeeQuoter.sol b/contracts/src/v0.8/ccip/FeeQuoter.sol index d2a6b77eff2..fc3fa328dba 100644 --- a/contracts/src/v0.8/ccip/FeeQuoter.sol +++ b/contracts/src/v0.8/ccip/FeeQuoter.sol @@ -715,8 +715,8 @@ contract FeeQuoter is AuthorizedCallers, IFeeQuoter, ITypeAndVersion, IReceiver, ) internal pure returns (uint256 dataAvailabilityCostUSD36Decimal) { // dataAvailabilityLengthBytes sums up byte lengths of fixed message fields and dynamic message fields. // Fixed message fields do account for the offset and length slot of the dynamic fields. - uint256 dataAvailabilityLengthBytes = Internal.ANY_2_EVM_MESSAGE_FIXED_BYTES + messageDataLength - + (numberOfTokens * Internal.ANY_2_EVM_MESSAGE_FIXED_BYTES_PER_TOKEN) + tokenTransferBytesOverhead; + uint256 dataAvailabilityLengthBytes = Internal.MESSAGE_FIXED_BYTES + messageDataLength + + (numberOfTokens * Internal.MESSAGE_FIXED_BYTES_PER_TOKEN) + tokenTransferBytesOverhead; // destDataAvailabilityOverheadGas is a separate config value for flexibility to be updated independently of message cost. // Its value is determined by CCIP lane implementation, e.g. the overhead data posted for OCR. diff --git a/contracts/src/v0.8/ccip/RMN.sol b/contracts/src/v0.8/ccip/RMN.sol deleted file mode 100644 index 3b9af7e0ce7..00000000000 --- a/contracts/src/v0.8/ccip/RMN.sol +++ /dev/null @@ -1,964 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {ITypeAndVersion} from "../shared/interfaces/ITypeAndVersion.sol"; -import {IRMN} from "./interfaces/IRMN.sol"; - -import {OwnerIsCreator} from "./../shared/access/OwnerIsCreator.sol"; - -import {EnumerableSet} from "../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/structs/EnumerableSet.sol"; - -// An active curse on this subject will cause isCursed() to return true. Use this subject if there is an issue with a -// remote chain, for which there exists a legacy lane contract deployed on the same chain as this RMN contract is -// deployed, relying on isCursed(). -bytes16 constant LEGACY_CURSE_SUBJECT = 0x01000000000000000000000000000000; - -// An active curse on this subject will cause isCursed() and isCursed(bytes16) to return true. Use this subject for -// issues affecting all of CCIP chains, or pertaining to the chain that this contract is deployed on, instead of using -// the local chain selector as a subject. -bytes16 constant GLOBAL_CURSE_SUBJECT = 0x01000000000000000000000000000001; - -// The curse vote address representing the owner in data structures, events and recorded votes. Remains constant, even -// if the owner changes. -address constant OWNER_CURSE_VOTE_ADDR = address(~uint160(0)); // 0xff...ff - -// The curse vote address used in an OwnerUnvoteToCurseRequest to lift a curse, if there is no active curse votes for -// the subject that we are able to unvote, but the conditions for an active curse no longer hold. -address constant LIFT_CURSE_VOTE_ADDR = address(0); - -/// @dev This contract is owned by RMN, if changing, please notify the RMN maintainers. -// solhint-disable chainlink-solidity/explicit-returns -contract RMN is IRMN, OwnerIsCreator, ITypeAndVersion { - using EnumerableSet for EnumerableSet.AddressSet; - - // STATIC CONFIG - string public constant override typeAndVersion = "RMN 1.5.0"; - - uint256 private constant MAX_NUM_VOTERS = 16; - - // MAGIC VALUES - bytes28 private constant NO_VOTES_CURSES_HASH = bytes28(0); - - // DYNAMIC CONFIG - /// @notice blessVoteAddr and curseVoteAddr can't be 0. Additionally curseVoteAddr can't be LIFT_CURSE_VOTE_ADDR or - /// OWNER_CURSE_VOTE_ADDR. At least one of blessWeight & curseWeight must be non-zero, i.e., a voter could only vote - /// to bless, or only vote to curse, or both vote to bless and vote to curse. - struct Voter { - // This is the address the voter should use to call voteToBless. - address blessVoteAddr; - // This is the address the voter should use to call voteToCurse. - address curseVoteAddr; - // The weight of this voter's vote for blessing. - uint8 blessWeight; - // The weight of this voter's vote for cursing. - uint8 curseWeight; - } - - struct Config { - Voter[] voters; - // When the total weight of voters that have voted to bless a tagged root reaches - // or exceeds blessWeightThreshold, the tagged root becomes blessed. - uint16 blessWeightThreshold; - // When the total weight of voters that have voted to curse a subject reaches or - // exceeds curseWeightThreshold, the subject becomes cursed. - uint16 curseWeightThreshold; - } - - struct VersionedConfig { - Config config; - // The version is incremented every time the config changes. - // The initial configuration on the contract will have configVersion == 1. - uint32 configVersion; - // The block number at which the config was last set. Helps the offchain - // code check that the config was set in a stable block or double-check - // that it has the correct config by querying logs at that block number. - uint32 blockNumber; - } - - VersionedConfig private s_versionedConfig; - - // STATE - struct BlesserRecord { - // The config version at which this BlesserRecord was last set. A blesser - // is considered active iff this configVersion equals - // s_versionedConfig.configVersion. - uint32 configVersion; - uint8 weight; - uint8 index; - } - - mapping(address blessVoteAddr => BlesserRecord blesserRecord) private s_blesserRecords; - - struct BlessVoteProgress { - // This particular ordering saves us ~400 gas per voteToBless call, compared to the bool being at the bottom, even - // though the size of the struct is the same. - bool weightThresholdMet; - // A BlessVoteProgress is considered invalid if weightThresholdMet is false when - // s_versionedConfig.configVersion changes. we don't want old in-progress - // votes to continue when we set a new config! - // The config version at which the bless vote for a tagged root was initiated. - uint32 configVersion; - uint16 accumulatedWeight; - // Care must be taken that the bitmap has at least as many bits as MAX_NUM_VOTERS. - // uint200 is much larger than we need, but it saves us ~100 gas per voteToBless call to fill the word instead of - // using a smaller type. - // _bitmapGet(voterBitmap, i) = true indicates that the i-th voter has voted to bless - uint200 voterBitmap; - } - - mapping(bytes32 taggedRootHash => BlessVoteProgress blessVoteProgress) private s_blessVoteProgressByTaggedRootHash; - - // Any tagged root with a commit store included in s_permaBlessedCommitStores will be considered automatically - // blessed. - EnumerableSet.AddressSet private s_permaBlessedCommitStores; - - struct CurserRecord { - bool active; - uint8 weight; - mapping(bytes16 curseId => bool used) usedCurseIds; // retained across config changes - } - - mapping(address curseVoteAddr => CurserRecord curserRecord) private s_curserRecords; - - struct ConfigVersionAndCursesHash { - uint32 configVersion; // configVersion != s_versionedConfig.configVersion means no active vote - bytes28 cursesHash; // bytes28(0) means no active vote; truncated so that ConfigVersionAndCursesHash fits in a word - } - - struct CurseVoteProgress { - uint32 configVersion; // upon config change, lazy set to new config version - uint16 curseWeightThreshold; // upon config change, lazy set to new config value - uint16 accumulatedWeight; // upon config change, lazy set to 0 - // A curse becomes active after either: - // - sum([voter.weight for voter who voted in current config]) >= curseWeightThreshold - // - ownerCurse is invoked - // Once a curse is active, only the owner can lift it. - bool curseActive; // retained across config changes - mapping(address => ConfigVersionAndCursesHash) latestVoteToCurseByCurseVoteAddr; // retained across config changes - } - - mapping(bytes16 subject => CurseVoteProgress curseVoteProgress) private - s_potentiallyOutdatedCurseVoteProgressBySubject; - - // We intentionally use a struct here, even though it contains a single field, to make it obvious to future editors - // that there is space for more fields. - struct CurseHotVars { - uint64 numSubjectsCursed; // incremented by voteToCurse, ownerCurse; decremented by ownerUnvoteToCurse - } - - CurseHotVars private s_curseHotVars; - - enum RecordedCurseRelatedOpTag { - // A vote to curse, through either voteToCurse or ownerCurse. - VoteToCurse, - // An unvote to curse, through unvoteToCurse. - UnvoteToCurse, - // An unvote to curse, through ownerUnvoteToCurse, which was not forced (forceUnvote=false). - OwnerUnvoteToCurseUnforced, - // An unvote to curse, through ownerUnvoteToCurse, which was forced (forceUnvote=true). - OwnerUnvoteToCurseForced, - // A configuration change. - // - // For subjects that are not cursed when this happens, past votes do not get accounted for in the new configuration. - // If a voter votes during the new configuration, their curses hash will restart from NO_VOTES_CURSES_HASH. - // - // For subjects that are cursed when this happens, past votes get accounted for. - // If a voter votes during the new configuration, their curses hash will continue from its old value. - SetConfig - } - - /// @notice Provides the ability to quickly reconstruct the curse-related state of the contract offchain, without - /// having to replay all past events. Replaying past events often takes long, and in some cases might even be - /// infeasible due to log pruning. - /// - /// @dev We could save some gas by omitting some fields and instead using them as mapping keys, but we would lose the - /// cross-voter ordering, or cross-subject ordering, or cross-vote/unvote ordering. - struct RecordedCurseRelatedOp { - RecordedCurseRelatedOpTag tag; - uint64 blockTimestamp; - bool cursed; // whether the subject is cursed after this op; if tag in {SetConfig}, will be false - address curseVoteAddr; // if tag in {SetConfig}, will be address(0) - bytes16 subject; // if tag in {SetConfig}, will be bytes16(0) - bytes16 curseId; // if tag in {SetConfig, UnvoteToCurse, OwnerUnvoteToCurseUnforced, OwnerUnvoteToCurseForced}, will be bytes16(0) - } - - RecordedCurseRelatedOp[] private s_recordedCurseRelatedOps; - - /// @dev This function is to _ONLY_ be called in order to determine if a curse should become active upon a - /// vote-to-curse, or a curse should be deactivated upon an owner-unvote-to-curse. - /// Other reasons for a curse to be active, which are not covered here: - /// 1. Cursedness is retained from a prior config. - /// 2. The curse weight threshold was met at some point, which activated a curse, and enough voters unvoted to curse - /// such that the curse weight threshold is no longer met. - function _shouldCurseBeActive(CurseVoteProgress storage sptr_upToDateCurseVoteProgress) internal view returns (bool) { - return sptr_upToDateCurseVoteProgress.latestVoteToCurseByCurseVoteAddr[OWNER_CURSE_VOTE_ADDR].cursesHash - != NO_VOTES_CURSES_HASH - || sptr_upToDateCurseVoteProgress.accumulatedWeight >= sptr_upToDateCurseVoteProgress.curseWeightThreshold; - } - - /// @dev It might be the case that due to the lazy update of curseVoteProgress, a curse is active even though - /// _shouldCurseBeActive(curseVoteProgress) is false, i.e., the owner has no active vote to curse and the curse - /// weight threshold has not been met. - function _getUpToDateCurseVoteProgress( - uint32 configVersion, - bytes16 subject - ) internal returns (CurseVoteProgress storage) { - CurseVoteProgress storage sptr_curseVoteProgress = s_potentiallyOutdatedCurseVoteProgressBySubject[subject]; - if (configVersion != sptr_curseVoteProgress.configVersion) { - sptr_curseVoteProgress.configVersion = configVersion; - sptr_curseVoteProgress.curseWeightThreshold = s_versionedConfig.config.curseWeightThreshold; - sptr_curseVoteProgress.accumulatedWeight = 0; - - if (sptr_curseVoteProgress.curseActive) { - // If a curse was active, count past votes to curse and retain the curses hash for cursers who are part of the - // new config. - Config storage sptr_config = s_versionedConfig.config; - for (uint256 i = 0; i < sptr_config.voters.length; ++i) { - Voter storage sptr_voter = sptr_config.voters[i]; - ConfigVersionAndCursesHash storage sptr_cvch = - sptr_curseVoteProgress.latestVoteToCurseByCurseVoteAddr[sptr_voter.curseVoteAddr]; - if (sptr_cvch.configVersion < configVersion && sptr_cvch.cursesHash != NO_VOTES_CURSES_HASH) { - // `< configVersion` instead of `== configVersion-1`, because there might have been multiple config changes - // without a lazy update of our subject. This has the side effect of retaining votes from very old configs - // that we might not really intend to retain, but these can be removed by the owner later. - sptr_cvch.configVersion = configVersion; - sptr_curseVoteProgress.accumulatedWeight += sptr_voter.curseWeight; - } - } - // We don't need to think about OWNER_CURSE_VOTE_ADDR here, because its ConfigVersionAndCursesHash counts even - // if the configVersion is not the current config version, in contrast to regular voters. - // It's an irregularity, but it saves us > 5k gas (if the owner had previously voted) for the unlucky voter who - // enters this branch. - } else { - // If a curse was not active, we don't count past votes to curse for voters who are part of the new config. - // Their curses hash will be restart from NO_VOTES_CURSES_HASH when they vote to curse again. - // We expect that the offchain code will revote to curse in case it voted to curse, and the vote to curse was - // lost due to any reason, including a config change when the curse was not yet active. - } - } - return sptr_curseVoteProgress; - } - - // EVENTS, ERRORS - - event ConfigSet(uint32 indexed configVersion, Config config); - - error InvalidConfig(); - - event TaggedRootBlessed(uint32 indexed configVersion, IRMN.TaggedRoot taggedRoot, uint16 accumulatedWeight); - event TaggedRootBlessVotesReset(uint32 indexed configVersion, IRMN.TaggedRoot taggedRoot, bool wasBlessed); - event VotedToBless(uint32 indexed configVersion, address indexed voter, IRMN.TaggedRoot taggedRoot, uint8 weight); - - event VotedToCurse( - uint32 indexed configVersion, - address indexed voter, - bytes16 subject, - bytes16 curseId, - uint8 weight, - uint64 blockTimestamp, - bytes28 cursesHash, - uint16 accumulatedWeight - ); - event UnvotedToCurse( - uint32 indexed configVersion, - address indexed voter, - bytes16 subject, - uint8 weight, - bytes28 cursesHash, - uint16 remainingAccumulatedWeight - ); - event SkippedUnvoteToCurse(address indexed voter, bytes16 subject, bytes28 onchainCursesHash, bytes28 cursesHash); - event Cursed(uint32 indexed configVersion, bytes16 subject, uint64 blockTimestamp); - event CurseLifted(bytes16 subject); - - // These events make it easier for offchain logic to discover that it performs - // the same actions multiple times. - event AlreadyVotedToBless(uint32 indexed configVersion, address indexed voter, IRMN.TaggedRoot taggedRoot); - event AlreadyBlessed(uint32 indexed configVersion, address indexed voter, IRMN.TaggedRoot taggedRoot); - - // Emitted by ownerRemoveThenAddPermaBlessedCommitStores. - event PermaBlessedCommitStoreAdded(address commitStore); - event PermaBlessedCommitStoreRemoved(address commitStore); - - error ReusedCurseId(address voter, bytes16 curseId); - error UnauthorizedVoter(address voter); - error VoteToBlessNoop(); - error VoteToCurseNoop(); - error UnvoteToCurseNoop(); - error VoteToBlessForbiddenDuringActiveGlobalCurse(); - - /// @notice Thrown when subjects are not a strictly increasing monotone sequence. - // Prevents a subject from receiving multiple votes to curse with the same curse id. - error SubjectsMustBeStrictlyIncreasing(); - - constructor(Config memory config) { - { - // Ensure that the bitmap is large enough to hold MAX_NUM_VOTERS. - // We do this in the constructor because MAX_NUM_VOTERS is constant. - BlessVoteProgress memory vp = BlessVoteProgress({ - configVersion: 0, - voterBitmap: type(uint200).max, // will not compile if it doesn't fit - accumulatedWeight: 0, - weightThresholdMet: false - }); - assert(vp.voterBitmap >> (MAX_NUM_VOTERS - 1) >= 1); - } - _setConfig(config); - } - - function _bitmapGet(uint200 bitmap, uint8 index) internal pure returns (bool) { - assert(index < MAX_NUM_VOTERS); - return bitmap & (uint200(1) << index) != 0; - } - - function _bitmapSet(uint200 bitmap, uint8 index) internal pure returns (uint200) { - assert(index < MAX_NUM_VOTERS); - return bitmap | (uint200(1) << index); - } - - function _bitmapCount(uint200 bitmap) internal pure returns (uint8 oneBits) { - assert(bitmap < 1 << MAX_NUM_VOTERS); - // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan - for (; bitmap != 0; ++oneBits) { - bitmap &= bitmap - 1; - } - } - - function _taggedRootHash(IRMN.TaggedRoot memory taggedRoot) internal pure returns (bytes32) { - return keccak256(abi.encode(taggedRoot.commitStore, taggedRoot.root)); - } - - function _cursesHash(bytes28 prevCursesHash, bytes16 curseId) internal pure returns (bytes28) { - return bytes28(keccak256(abi.encode(prevCursesHash, curseId))); - } - - function _blockTimestamp() internal view returns (uint64) { - return uint64(block.timestamp); - } - - /// @param taggedRoots A tagged root is hashed as `keccak256(abi.encode(taggedRoot.commitStore - /// /* address */, taggedRoot.root /* bytes32 */))`. - /// @notice Tagged roots which are already (voted to be) blessed are skipped and emit corresponding events. In case - /// the call has no effect, i.e., all passed tagged roots are skipped, the function reverts with a `VoteToBlessNoop`. - function voteToBless(IRMN.TaggedRoot[] calldata taggedRoots) external { - // If we have an active global curse, something is really wrong. Let's err on the - // side of caution and not accept further blessings during this time of - // uncertainty. - if (isCursed(GLOBAL_CURSE_SUBJECT)) revert VoteToBlessForbiddenDuringActiveGlobalCurse(); - - uint32 configVersion = s_versionedConfig.configVersion; - BlesserRecord memory blesserRecord = s_blesserRecords[msg.sender]; - if (blesserRecord.configVersion != configVersion) revert UnauthorizedVoter(msg.sender); - - bool noop = true; - for (uint256 i = 0; i < taggedRoots.length; ++i) { - IRMN.TaggedRoot memory taggedRoot = taggedRoots[i]; - bytes32 taggedRootHash = _taggedRootHash(taggedRoot); - BlessVoteProgress memory voteProgress = s_blessVoteProgressByTaggedRootHash[taggedRootHash]; - if (voteProgress.weightThresholdMet) { - // We don't revert here because it's unreasonable to expect from the - // voter to know exactly when to stop voting. Most likely when they - // voted they didn't realize the threshold would be reached by the time - // their vote was counted. - // Additionally, there might be other tagged roots for which votes might - // count, and we want to allow that to happen. - emit AlreadyBlessed(configVersion, msg.sender, taggedRoot); - continue; - } else if (voteProgress.configVersion != configVersion) { - // Note that voteProgress.weightThresholdMet must be false at this point - - // If votes were received while an older config was in effect, - // invalidate them and start from scratch. - // If votes were never received, set the current config version. - voteProgress = BlessVoteProgress({ - configVersion: configVersion, - voterBitmap: 0, - accumulatedWeight: 0, - weightThresholdMet: false - }); - } else if (_bitmapGet(voteProgress.voterBitmap, blesserRecord.index)) { - // We don't revert here because there might be other tagged roots for - // which votes might count, and we want to allow that to happen. - emit AlreadyVotedToBless(configVersion, msg.sender, taggedRoot); - continue; - } - noop = false; - voteProgress.voterBitmap = _bitmapSet(voteProgress.voterBitmap, blesserRecord.index); - voteProgress.accumulatedWeight += blesserRecord.weight; - emit VotedToBless(configVersion, msg.sender, taggedRoot, blesserRecord.weight); - if (voteProgress.accumulatedWeight >= s_versionedConfig.config.blessWeightThreshold) { - voteProgress.weightThresholdMet = true; - emit TaggedRootBlessed(configVersion, taggedRoot, voteProgress.accumulatedWeight); - } - s_blessVoteProgressByTaggedRootHash[taggedRootHash] = voteProgress; - } - - if (noop) { - revert VoteToBlessNoop(); - } - } - - /// @notice Can be called by the owner to remove unintentionally voted or even blessed tagged roots in a recovery - /// scenario. The owner must ensure that there are no in-flight transactions by RMN nodes voting for any of the - /// taggedRoots before calling this function, as such in-flight transactions could lead to the roots becoming - /// re-blessed shortly after the call to this function, contrary to the original intention. - function ownerResetBlessVotes(IRMN.TaggedRoot[] calldata taggedRoots) external onlyOwner { - uint32 configVersion = s_versionedConfig.configVersion; - for (uint256 i = 0; i < taggedRoots.length; ++i) { - IRMN.TaggedRoot memory taggedRoot = taggedRoots[i]; - bytes32 taggedRootHash = _taggedRootHash(taggedRoot); - BlessVoteProgress memory voteProgress = s_blessVoteProgressByTaggedRootHash[taggedRootHash]; - delete s_blessVoteProgressByTaggedRootHash[taggedRootHash]; - bool wasBlessed = voteProgress.weightThresholdMet; - if (voteProgress.configVersion == configVersion || wasBlessed) { - emit TaggedRootBlessVotesReset(configVersion, taggedRoot, wasBlessed); - } - } - } - - struct UnvoteToCurseRequest { - bytes16 subject; - bytes28 cursesHash; - } - - // For use in internal calls. - enum Privilege { - Owner, - Voter - } - - function _authorizedUnvoteToCurse( - Privilege priv, // Privilege.Owner during an ownerUnvoteToCurse call, Privilege.Voter during a unvoteToCurse call - uint32 configVersion, - address curseVoteAddr, - UnvoteToCurseRequest memory req, - bool forceUnvote, // true only during an ownerUnvoteToCurse call, when OwnerUnvoteToCurseRequest.forceUnvote is true - CurserRecord storage sptr_curserRecord, - CurseVoteProgress storage sptr_curseVoteProgress - ) internal returns (bool unvoted, bool curseLifted) { - { - assert(priv == Privilege.Voter || priv == Privilege.Owner); // sanity check - // Check that the supplied arguments are feasible for our privilege. - if (forceUnvote || curseVoteAddr == OWNER_CURSE_VOTE_ADDR || curseVoteAddr == LIFT_CURSE_VOTE_ADDR) { - assert(priv == Privilege.Owner); - } - } - - ConfigVersionAndCursesHash memory cvch = sptr_curseVoteProgress.latestVoteToCurseByCurseVoteAddr[curseVoteAddr]; - - // First, try to unvote. - if ( - sptr_curserRecord.active && (curseVoteAddr == OWNER_CURSE_VOTE_ADDR || cvch.configVersion == configVersion) - && cvch.cursesHash != NO_VOTES_CURSES_HASH && (cvch.cursesHash == req.cursesHash || forceUnvote) - ) { - unvoted = true; - delete sptr_curseVoteProgress.latestVoteToCurseByCurseVoteAddr[curseVoteAddr]; - // Assumes: s_curserRecords[OWNER_CURSE_VOTE_ADDR].weight == 0, enforced by _setConfig - sptr_curseVoteProgress.accumulatedWeight -= sptr_curserRecord.weight; - - emit UnvotedToCurse( - configVersion, - curseVoteAddr, - req.subject, - sptr_curserRecord.weight, - req.cursesHash, - sptr_curseVoteProgress.accumulatedWeight - ); - } - - // If we have owner privilege, and the conditions for the curse to be active no longer hold, we are able to lift the - // curse. - bool shouldTryToLiftCurse = priv == Privilege.Owner && (unvoted || curseVoteAddr == LIFT_CURSE_VOTE_ADDR); - - if (shouldTryToLiftCurse && sptr_curseVoteProgress.curseActive && !_shouldCurseBeActive(sptr_curseVoteProgress)) { - curseLifted = true; - sptr_curseVoteProgress.curseActive = false; - --s_curseHotVars.numSubjectsCursed; - emit CurseLifted(req.subject); - } - - if (unvoted || curseLifted) { - RecordedCurseRelatedOpTag tag; - if (priv == Privilege.Owner) { - if (forceUnvote) { - tag = RecordedCurseRelatedOpTag.OwnerUnvoteToCurseForced; - } else { - tag = RecordedCurseRelatedOpTag.OwnerUnvoteToCurseUnforced; - } - } else if (priv == Privilege.Voter) { - tag = RecordedCurseRelatedOpTag.UnvoteToCurse; - } else { - // solhint-disable-next-line gas-custom-errors, reason-string - revert(); // assumption violation - } - s_recordedCurseRelatedOps.push( - RecordedCurseRelatedOp({ - tag: tag, - cursed: sptr_curseVoteProgress.curseActive, - curseVoteAddr: curseVoteAddr, - curseId: bytes16(0), - subject: req.subject, - blockTimestamp: _blockTimestamp() - }) - ); - } else { - emit SkippedUnvoteToCurse(curseVoteAddr, req.subject, cvch.cursesHash, req.cursesHash); - } - } - - /// @notice Can be called by a curser to remove unintentional votes to curse. - /// We expect this to be called very rarely, e.g. in case of a bug in the - /// offchain code causing false voteToCurse calls. - /// @notice Should be called from curser's corresponding curseVoteAddr. - function unvoteToCurse(UnvoteToCurseRequest[] memory unvoteToCurseRequests) external { - address curseVoteAddr = msg.sender; - CurserRecord storage sptr_curserRecord = s_curserRecords[curseVoteAddr]; - - if (!sptr_curserRecord.active) revert UnauthorizedVoter(curseVoteAddr); - - uint32 configVersion = s_versionedConfig.configVersion; - bool anyVoteWasUnvoted = false; - for (uint256 i = 0; i < unvoteToCurseRequests.length; ++i) { - UnvoteToCurseRequest memory req = unvoteToCurseRequests[i]; - CurseVoteProgress storage sptr_curseVoteProgress = _getUpToDateCurseVoteProgress(configVersion, req.subject); - (bool unvoted, bool curseLifted) = _authorizedUnvoteToCurse( - Privilege.Voter, configVersion, curseVoteAddr, req, false, sptr_curserRecord, sptr_curseVoteProgress - ); - assert(!curseLifted); // assumption violation: voters can't lift curses - anyVoteWasUnvoted = anyVoteWasUnvoted || unvoted; - } - - if (!anyVoteWasUnvoted) { - revert UnvoteToCurseNoop(); - } - } - - /// @notice A vote to curse is appropriate during unhealthy blockchain conditions - /// (eg. finality violations). - function voteToCurse(bytes16 curseId, bytes16[] memory subjects) external { - address curseVoteAddr = msg.sender; - assert(curseVoteAddr != OWNER_CURSE_VOTE_ADDR); - CurserRecord storage sptr_curserRecord = s_curserRecords[curseVoteAddr]; - if (!sptr_curserRecord.active) revert UnauthorizedVoter(curseVoteAddr); - _authorizedVoteToCurse(curseVoteAddr, curseId, subjects, sptr_curserRecord); - } - - function _authorizedVoteToCurse( - address curseVoteAddr, - bytes16 curseId, - bytes16[] memory subjects, - CurserRecord storage sptr_curserRecord - ) internal { - if (subjects.length == 0) revert VoteToCurseNoop(); - - if (sptr_curserRecord.usedCurseIds[curseId]) revert ReusedCurseId(curseVoteAddr, curseId); - sptr_curserRecord.usedCurseIds[curseId] = true; - - // NOTE: We could pack configVersion into CurserRecord that we already load in the beginning of this function to - // avoid the following extra storage read for it, but since voteToCurse is not on the hot path we'd rather keep - // things simple. - uint32 configVersion = s_versionedConfig.configVersion; - for (uint256 i = 0; i < subjects.length; ++i) { - if (i >= 1 && !(subjects[i - 1] < subjects[i])) { - // Prevents a subject from receiving multiple votes to curse with the same curse id. - revert SubjectsMustBeStrictlyIncreasing(); - } - - bytes16 subject = subjects[i]; - CurseVoteProgress storage sptr_curseVoteProgress = _getUpToDateCurseVoteProgress(configVersion, subject); - ConfigVersionAndCursesHash memory cvch = sptr_curseVoteProgress.latestVoteToCurseByCurseVoteAddr[curseVoteAddr]; - bytes28 prevCursesHash; - if ( - (curseVoteAddr != OWNER_CURSE_VOTE_ADDR && cvch.configVersion < configVersion) - || cvch.cursesHash == NO_VOTES_CURSES_HASH - ) { - // if owner's first vote, or if voter's first vote in this config version - prevCursesHash = NO_VOTES_CURSES_HASH; // start hashchain from scratch, explicit - sptr_curseVoteProgress.accumulatedWeight += sptr_curserRecord.weight; - } else { - // we've already accounted for the weight - prevCursesHash = cvch.cursesHash; - } - sptr_curseVoteProgress.latestVoteToCurseByCurseVoteAddr[curseVoteAddr] = cvch = - ConfigVersionAndCursesHash({configVersion: configVersion, cursesHash: _cursesHash(prevCursesHash, curseId)}); - emit VotedToCurse( - configVersion, - curseVoteAddr, - subject, - curseId, - sptr_curserRecord.weight, - _blockTimestamp(), - cvch.cursesHash, - sptr_curseVoteProgress.accumulatedWeight - ); - - if ( - prevCursesHash == NO_VOTES_CURSES_HASH && !sptr_curseVoteProgress.curseActive - && _shouldCurseBeActive(sptr_curseVoteProgress) - ) { - sptr_curseVoteProgress.curseActive = true; - ++s_curseHotVars.numSubjectsCursed; - emit Cursed(configVersion, subject, _blockTimestamp()); - } - - s_recordedCurseRelatedOps.push( - RecordedCurseRelatedOp({ - tag: RecordedCurseRelatedOpTag.VoteToCurse, - cursed: sptr_curseVoteProgress.curseActive, - curseVoteAddr: curseVoteAddr, - curseId: curseId, - subject: subject, - blockTimestamp: _blockTimestamp() - }) - ); - } - } - - /// @notice Enables the owner to immediately have the system enter the cursed state. - function ownerCurse(bytes16 curseId, bytes16[] memory subjects) external onlyOwner { - address curseVoteAddr = OWNER_CURSE_VOTE_ADDR; - CurserRecord storage sptr_curserRecord = s_curserRecords[curseVoteAddr]; - // no need to check if sptr_curserRecord.active, we must have the onlyOwner modifier - _authorizedVoteToCurse(curseVoteAddr, curseId, subjects, sptr_curserRecord); - } - - // Set curseVoteAddr=LIFT_CURSE_VOTE_ADDR, cursesHash=bytes28(0), to reset curseActive if it can be reset. Useful if - // all voters have unvoted to curse on their own and the curse can now be lifted without any individual votes that can - // be unvoted. - // solhint-disable-next-line gas-struct-packing - struct OwnerUnvoteToCurseRequest { - address curseVoteAddr; - UnvoteToCurseRequest unit; - bool forceUnvote; - } - - /// @notice Enables the owner to remove curse votes. After the curse votes are removed, - /// this function will check whether the curse is still valid and restore the uncursed state if possible. - /// This function also enables the owner to lift a curse created through ownerCurse. - function ownerUnvoteToCurse(OwnerUnvoteToCurseRequest[] memory ownerUnvoteToCurseRequests) external onlyOwner { - bool anyCurseWasLifted = false; - bool anyVoteWasUnvoted = false; - uint32 configVersion = s_versionedConfig.configVersion; - for (uint256 i = 0; i < ownerUnvoteToCurseRequests.length; ++i) { - OwnerUnvoteToCurseRequest memory req = ownerUnvoteToCurseRequests[i]; - CurseVoteProgress storage sptr_curseVoteProgress = _getUpToDateCurseVoteProgress(configVersion, req.unit.subject); - (bool unvoted, bool curseLifted) = _authorizedUnvoteToCurse( - Privilege.Owner, - configVersion, - req.curseVoteAddr, - req.unit, - req.forceUnvote, - s_curserRecords[req.curseVoteAddr], - sptr_curseVoteProgress - ); - anyVoteWasUnvoted = anyVoteWasUnvoted || unvoted; - anyCurseWasLifted = anyCurseWasLifted || curseLifted; - } - - if (anyCurseWasLifted) { - // Invalidate all in-progress votes to bless or curse by bumping the config version. - // They might have been based on false information about the source chain - // (e.g. in case of a finality violation). - _setConfig(s_versionedConfig.config); - } - - if (!(anyVoteWasUnvoted || anyCurseWasLifted)) { - revert UnvoteToCurseNoop(); - } - } - - function setConfig(Config memory config) external onlyOwner { - _setConfig(config); - } - - /// @notice Any tagged root with a commit store included in this array will be considered automatically blessed. - function getPermaBlessedCommitStores() external view returns (address[] memory) { - return s_permaBlessedCommitStores.values(); - } - - /// @notice The ordering of parameters is important. First come the commit stores to remove, then the commit stores to - /// add. - function ownerRemoveThenAddPermaBlessedCommitStores( - address[] memory removes, - address[] memory adds - ) external onlyOwner { - for (uint256 i = 0; i < removes.length; ++i) { - if (s_permaBlessedCommitStores.remove(removes[i])) { - emit PermaBlessedCommitStoreRemoved(removes[i]); - } - } - for (uint256 i = 0; i < adds.length; ++i) { - if (s_permaBlessedCommitStores.add(adds[i])) { - emit PermaBlessedCommitStoreAdded(adds[i]); - } - } - } - - /// @inheritdoc IRMN - function isBlessed(IRMN.TaggedRoot calldata taggedRoot) external view returns (bool) { - return s_blessVoteProgressByTaggedRootHash[_taggedRootHash(taggedRoot)].weightThresholdMet - || s_permaBlessedCommitStores.contains(taggedRoot.commitStore); - } - - /// @inheritdoc IRMN - function isCursed() external view returns (bool) { - if (s_curseHotVars.numSubjectsCursed == 0) { - return false; // happy path costs a single SLOAD - } else { - return s_potentiallyOutdatedCurseVoteProgressBySubject[GLOBAL_CURSE_SUBJECT].curseActive - || s_potentiallyOutdatedCurseVoteProgressBySubject[LEGACY_CURSE_SUBJECT].curseActive; - } - } - - /// @inheritdoc IRMN - function isCursed(bytes16 subject) public view returns (bool) { - if (s_curseHotVars.numSubjectsCursed == 0) { - return false; // happy path costs a single SLOAD - } else { - return s_potentiallyOutdatedCurseVoteProgressBySubject[GLOBAL_CURSE_SUBJECT].curseActive - || s_potentiallyOutdatedCurseVoteProgressBySubject[subject].curseActive; - } - } - - /// @notice Config version might be incremented for many reasons, including - /// the lifting of a curse, or a regular config change. - function getConfigDetails() external view returns (uint32 version, uint32 blockNumber, Config memory config) { - version = s_versionedConfig.configVersion; - blockNumber = s_versionedConfig.blockNumber; - config = s_versionedConfig.config; - } - - /// @return blessVoteAddrs addresses of voters, will be empty if voting took place with an older config version - /// @return accumulatedWeight sum of weights of voters, will be zero if voting took place with an older config version - /// @return blessed will be accurate regardless of when voting took place - /// @dev This is a helper method for offchain code so efficiency is not really a concern. - function getBlessProgress( - IRMN.TaggedRoot calldata taggedRoot - ) external view returns (address[] memory blessVoteAddrs, uint16 accumulatedWeight, bool blessed) { - bytes32 taggedRootHash = _taggedRootHash(taggedRoot); - BlessVoteProgress memory progress = s_blessVoteProgressByTaggedRootHash[taggedRootHash]; - blessed = progress.weightThresholdMet; - if (progress.configVersion == s_versionedConfig.configVersion) { - accumulatedWeight = progress.accumulatedWeight; - uint200 bitmap = progress.voterBitmap; - blessVoteAddrs = new address[](_bitmapCount(bitmap)); - Voter[] memory voters = s_versionedConfig.config.voters; - uint256 j = 0; - for (uint8 i = 0; i < voters.length; ++i) { - if (_bitmapGet(bitmap, i)) { - blessVoteAddrs[j] = voters[i].blessVoteAddr; - ++j; - } - } - } - } - - /// @return curseVoteAddrs the curseVoteAddr of each voter with an active vote to curse - /// @return cursesHashes the i-th value is the curses hash of curseVoteAddrs[i] - /// @return accumulatedWeight the accumulated weight of all voters with an active vote to curse who are part of the - /// current config - /// @return cursed might be true even if the owner has no active vote and accumulatedWeight < curseWeightThreshold, - /// due to a retained curse from a prior config - /// @dev This is a helper method for offchain code so efficiency is not really a concern. - function getCurseProgress( - bytes16 subject - ) - external - view - returns (address[] memory curseVoteAddrs, bytes28[] memory cursesHashes, uint16 accumulatedWeight, bool cursed) - { - uint32 configVersion = s_versionedConfig.configVersion; - Config memory config = s_versionedConfig.config; - // Can't use _getUpToDateCurseVoteProgress here because we can't call a non-view function from within a view. - // So we get to repeat some accounting. - CurseVoteProgress storage outdatedCurseVoteProgress = s_potentiallyOutdatedCurseVoteProgressBySubject[subject]; - - cursed = outdatedCurseVoteProgress.curseActive; - - // See _getUpToDateCurseVoteProgress for more context. - bool shouldCountVotesFromOlderConfigs = outdatedCurseVoteProgress.configVersion < configVersion && cursed; - - // A play in two acts, because we can't push to arrays in memory, so we need to precompute the array's length. - // First act: we count the number of cursers, i.e., voters with active vote. - // Second act: push the cursers to the arrays, sum their weights. - - uint256 numCursers = 0; // we reuse this variable for writing to perserve stack space - accumulatedWeight = 0; - for (uint256 act = 1; act <= 2; ++act) { - uint256 i = config.voters.length; // not config.voters.length-1 to account for the owner - while (true) { - address curseVoteAddr; - uint8 weight; - if (i < config.voters.length) { - curseVoteAddr = config.voters[i].curseVoteAddr; - weight = config.voters[i].curseWeight; - } else { - // Allows us to include the owner's vote and curses hash in the result. - curseVoteAddr = OWNER_CURSE_VOTE_ADDR; - weight = 0; - } - - ConfigVersionAndCursesHash memory cvch = - outdatedCurseVoteProgress.latestVoteToCurseByCurseVoteAddr[curseVoteAddr]; - bool hasActiveVote = ( - shouldCountVotesFromOlderConfigs || cvch.configVersion == configVersion - || curseVoteAddr == OWNER_CURSE_VOTE_ADDR - ) && cvch.cursesHash != NO_VOTES_CURSES_HASH; - if (hasActiveVote) { - if (act == 1) { - ++numCursers; - } else if (act == 2) { - accumulatedWeight += weight; - --numCursers; - curseVoteAddrs[numCursers] = curseVoteAddr; - cursesHashes[numCursers] = cvch.cursesHash; - } else { - // solhint-disable-next-line gas-custom-errors, reason-string - revert(); // assumption violation - } - } - - if (i > 0) { - --i; - } else { - break; - } - } - - if (act == 1) { - // We are done counting at this point, initialize the arrays for the second act that follows immediately after. - curseVoteAddrs = new address[](numCursers); - cursesHashes = new bytes28[](numCursers); - } - } - } - - /// @notice Returns the number of subjects that are currently cursed. - function getCursedSubjectsCount() external view returns (uint256) { - return s_curseHotVars.numSubjectsCursed; - } - - /// @dev This is a helper method for offchain code to know what arguments to use for getRecordedCurseRelatedOps. - function getRecordedCurseRelatedOpsCount() external view returns (uint256) { - return s_recordedCurseRelatedOps.length; - } - - /// @dev This is a helper method for offchain code so efficiency is not really a concern. - /// @dev Returns s_recordedCurseRelatedOps[offset:offset+limit]. - function getRecordedCurseRelatedOps( - uint256 offset, - uint256 limit - ) external view returns (RecordedCurseRelatedOp[] memory) { - uint256 pageLen; - if (offset + limit <= s_recordedCurseRelatedOps.length) { - pageLen = limit; - } else if (offset < s_recordedCurseRelatedOps.length) { - pageLen = s_recordedCurseRelatedOps.length - offset; - } else { - pageLen = 0; - } - RecordedCurseRelatedOp[] memory page = new RecordedCurseRelatedOp[](pageLen); - for (uint256 i = 0; i < pageLen; ++i) { - page[i] = s_recordedCurseRelatedOps[offset + i]; - } - return page; - } - - function _validateConfig(Config memory config) internal pure returns (bool) { - if ( - config.voters.length == 0 || config.voters.length > MAX_NUM_VOTERS || config.blessWeightThreshold == 0 - || config.curseWeightThreshold == 0 - ) { - return false; - } - - uint256 totalBlessWeight = 0; - uint256 totalCurseWeight = 0; - address[] memory allAddrs = new address[](2 * config.voters.length); - for (uint256 i = 0; i < config.voters.length; ++i) { - Voter memory voter = config.voters[i]; - // The owner can always curse using the ownerCurse method, and is not supposed to be included in the voters list. - // Even though the intent is for the actual owner address to NOT be included in the voters list, we don't - // explicitly disallow curseVoteAddr == owner() here. Even if we did, the owner could transfer ownership of the - // contract, and so we couldn't guarantee that the owner is not eventually included in the voters list. - if ( - voter.blessVoteAddr == address(0) || voter.curseVoteAddr == address(0) - || voter.curseVoteAddr == LIFT_CURSE_VOTE_ADDR || voter.curseVoteAddr == OWNER_CURSE_VOTE_ADDR - || (voter.blessWeight == 0 && voter.curseWeight == 0) - ) { - return false; - } - allAddrs[2 * i + 0] = voter.blessVoteAddr; - allAddrs[2 * i + 1] = voter.curseVoteAddr; - totalBlessWeight += voter.blessWeight; - totalCurseWeight += voter.curseWeight; - } - for (uint256 i = 0; i < allAddrs.length; ++i) { - address allAddrs_i = allAddrs[i]; - for (uint256 j = i + 1; j < allAddrs.length; ++j) { - if (allAddrs_i == allAddrs[j]) { - return false; - } - } - } - - return totalBlessWeight >= config.blessWeightThreshold && totalCurseWeight >= config.curseWeightThreshold; - } - - function _setConfig(Config memory config) private { - if (!_validateConfig(config)) revert InvalidConfig(); - - // We can't directly assign s_versionedConfig.config to config - // because copying a memory array into storage is not supported. - { - s_versionedConfig.config.blessWeightThreshold = config.blessWeightThreshold; - s_versionedConfig.config.curseWeightThreshold = config.curseWeightThreshold; - while (s_versionedConfig.config.voters.length != 0) { - Voter memory voter = s_versionedConfig.config.voters[s_versionedConfig.config.voters.length - 1]; - delete s_blesserRecords[voter.blessVoteAddr]; - delete s_curserRecords[voter.curseVoteAddr]; // usedCurseIds mapping is retained, as intended - s_versionedConfig.config.voters.pop(); - } - for (uint256 i = 0; i < config.voters.length; ++i) { - s_versionedConfig.config.voters.push(config.voters[i]); - } - } - - ++s_versionedConfig.configVersion; - uint32 configVersion = s_versionedConfig.configVersion; - - for (uint8 i = 0; i < config.voters.length; ++i) { - Voter memory voter = config.voters[i]; - s_blesserRecords[voter.blessVoteAddr] = - BlesserRecord({configVersion: configVersion, index: i, weight: voter.blessWeight}); - { - CurserRecord storage sptr_curserRecord = s_curserRecords[voter.curseVoteAddr]; - // Solidity will not let us initialize as CurserRecord({...}) due to the nested mapping - sptr_curserRecord.active = true; - sptr_curserRecord.weight = voter.curseWeight; - } - } - { - // Initialize the owner's CurserRecord - // We could in principle perform this initialization once in the constructor instead, and save a small bit of gas. - // But configuration changes are relatively infrequent, and keeping the initialization here makes the contract's - // correctness easier to reason about. - CurserRecord storage sptr_ownerCurserRecord = s_curserRecords[OWNER_CURSE_VOTE_ADDR]; - sptr_ownerCurserRecord.active = true; // Assumed by vote/unvote-to-curse logic - sptr_ownerCurserRecord.weight = 0; // Assumed by vote/unvote-to-curse logic - } - s_versionedConfig.blockNumber = uint32(block.number); - emit ConfigSet(configVersion, config); - - s_recordedCurseRelatedOps.push( - RecordedCurseRelatedOp({ - tag: RecordedCurseRelatedOpTag.SetConfig, - blockTimestamp: _blockTimestamp(), - cursed: false, - curseVoteAddr: address(0), - curseId: bytes16(0), - subject: bytes16(0) - }) - ); - } -} diff --git a/contracts/src/v0.8/ccip/applications/SelfFundedPingPong.sol b/contracts/src/v0.8/ccip/applications/SelfFundedPingPong.sol deleted file mode 100644 index 09c81ad5de5..00000000000 --- a/contracts/src/v0.8/ccip/applications/SelfFundedPingPong.sol +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.24; - -import {Router} from "../Router.sol"; -import {Client} from "../libraries/Client.sol"; -import {EVM2EVMOnRamp} from "../onRamp/EVM2EVMOnRamp.sol"; -import {PingPongDemo} from "./PingPongDemo.sol"; - -import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; - -contract SelfFundedPingPong is PingPongDemo { - string public constant override typeAndVersion = "SelfFundedPingPong 1.5.0"; - - event Funded(); - event CountIncrBeforeFundingSet(uint8 countIncrBeforeFunding); - - // Defines the increase in ping pong count before self-funding is attempted. - // Set to 0 to disable auto-funding, auto-funding only works for ping-pongs that are set as NOPs in the onRamp. - uint8 private s_countIncrBeforeFunding; - - constructor(address router, IERC20 feeToken, uint8 roundTripsBeforeFunding) PingPongDemo(router, feeToken) { - // PingPong count increases by 2 for each round trip. - s_countIncrBeforeFunding = roundTripsBeforeFunding * 2; - } - - function _respond(uint256 pingPongCount) internal override { - if (pingPongCount & 1 == 1) { - emit Ping(pingPongCount); - } else { - emit Pong(pingPongCount); - } - - fundPingPong(pingPongCount); - - Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ - receiver: abi.encode(s_counterpartAddress), - data: abi.encode(pingPongCount), - tokenAmounts: new Client.EVMTokenAmount[](0), - extraArgs: "", - feeToken: address(s_feeToken) - }); - Router(getRouter()).ccipSend(s_counterpartChainSelector, message); - } - - /// @notice A function that is responsible for funding this contract. - /// The contract can only be funded if it is set as a nop in the target onRamp. - /// In case your contract is not a nop you can prevent this function from being called by setting s_countIncrBeforeFunding=0. - function fundPingPong(uint256 pingPongCount) public { - // If selfFunding is disabled, or ping pong count has not reached s_countIncrPerFunding, do not attempt funding. - if (s_countIncrBeforeFunding == 0 || pingPongCount < s_countIncrBeforeFunding) return; - - // Ping pong on one side will always be even, one side will always to odd. - if (pingPongCount % s_countIncrBeforeFunding <= 1) { - EVM2EVMOnRamp(Router(getRouter()).getOnRamp(s_counterpartChainSelector)).payNops(); - emit Funded(); - } - } - - function getCountIncrBeforeFunding() external view returns (uint8) { - return s_countIncrBeforeFunding; - } - - function setCountIncrBeforeFunding(uint8 countIncrBeforeFunding) external onlyOwner { - s_countIncrBeforeFunding = countIncrBeforeFunding; - emit CountIncrBeforeFundingSet(countIncrBeforeFunding); - } -} diff --git a/contracts/src/v0.8/ccip/applications/TokenProxy.sol b/contracts/src/v0.8/ccip/applications/TokenProxy.sol deleted file mode 100644 index 6fd26c076bc..00000000000 --- a/contracts/src/v0.8/ccip/applications/TokenProxy.sol +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.24; - -import {IRouterClient} from "../interfaces/IRouterClient.sol"; - -import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol"; -import {Client} from "../libraries/Client.sol"; - -import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; - -contract TokenProxy is OwnerIsCreator { - using SafeERC20 for IERC20; - - error InvalidToken(); - error NoDataAllowed(); - error GasShouldBeZero(); - - /// @notice The CCIP router contract - IRouterClient internal immutable i_ccipRouter; - /// @notice Only this token is allowed to be sent using this proxy - address internal immutable i_token; - - constructor(address router, address token) OwnerIsCreator() { - i_ccipRouter = IRouterClient(router); - i_token = token; - // Approve the router to spend an unlimited amount of tokens to reduce - // gas cost per tx. - IERC20(token).approve(router, type(uint256).max); - } - - /// @notice Simply forwards the request to the CCIP router and returns the result. - /// @param destinationChainSelector The destination chainSelector - /// @param message The cross-chain CCIP message including data and/or tokens - /// @return fee returns execution fee for the message delivery to destination chain, - /// denominated in the feeToken specified in the message. - /// @dev Reverts with appropriate reason upon invalid message. - function getFee( - uint64 destinationChainSelector, - Client.EVM2AnyMessage calldata message - ) external view returns (uint256 fee) { - _validateMessage(message); - return i_ccipRouter.getFee(destinationChainSelector, message); - } - - /// @notice Validates the message content, forwards it to the CCIP router and returns the result. - function ccipSend( - uint64 destinationChainSelector, - Client.EVM2AnyMessage calldata message - ) external payable returns (bytes32 messageId) { - _validateMessage(message); - if (message.feeToken != address(0)) { - // This path is probably warmed up already so the extra cost isn't too bad. - uint256 feeAmount = i_ccipRouter.getFee(destinationChainSelector, message); - IERC20(message.feeToken).safeTransferFrom(msg.sender, address(this), feeAmount); - IERC20(message.feeToken).approve(address(i_ccipRouter), feeAmount); - } - - // Transfer the tokens from the sender to this contract. - IERC20(message.tokenAmounts[0].token).transferFrom(msg.sender, address(this), message.tokenAmounts[0].amount); - - return i_ccipRouter.ccipSend{value: msg.value}(destinationChainSelector, message); - } - - /// @notice Validates the message content. - /// @dev Only allows a single token to be sent, and no data. - function _validateMessage(Client.EVM2AnyMessage calldata message) internal view { - if (message.tokenAmounts.length != 1 || message.tokenAmounts[0].token != i_token) revert InvalidToken(); - if (message.data.length > 0) revert NoDataAllowed(); - - if (message.extraArgs.length == 0 || bytes4(message.extraArgs) != Client.EVM_EXTRA_ARGS_V1_TAG) { - revert GasShouldBeZero(); - } - - if (abi.decode(message.extraArgs[4:], (Client.EVMExtraArgsV1)).gasLimit != 0) revert GasShouldBeZero(); - } - - /// @notice Returns the CCIP router contract. - function getRouter() external view returns (IRouterClient) { - return i_ccipRouter; - } - - /// @notice Returns the token that this proxy is allowed to send. - function getToken() external view returns (address) { - return i_token; - } -} diff --git a/contracts/src/v0.8/ccip/libraries/Internal.sol b/contracts/src/v0.8/ccip/libraries/Internal.sol index 90b36adb7b7..4459a27a9b9 100644 --- a/contracts/src/v0.8/ccip/libraries/Internal.sol +++ b/contracts/src/v0.8/ccip/libraries/Internal.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.4; import {MerkleMultiProof} from "../libraries/MerkleMultiProof.sol"; -import {Client} from "./Client.sol"; // Library for CCIP internal definitions common to multiple contracts. library Internal { @@ -42,8 +41,8 @@ library Internal { /// @notice A timestamped uint224 value that can contain several tightly packed fields. struct TimestampedPackedUint224 { - uint224 value; // ───────╮ Value in uint224, packed. - uint32 timestamp; // ────╯ Timestamp of the most recent price update. + uint224 value; // ──────╮ Value in uint224, packed. + uint32 timestamp; // ───╯ Timestamp of the most recent price update. } /// @dev Gas price is stored in 112-bit unsigned int. uint224 can pack 2 prices. @@ -51,11 +50,6 @@ library Internal { /// Using uint8 type, which cannot be higher than other bit shift operands, to avoid shift operand type warning. uint8 public constant GAS_PRICE_BITS = 112; - struct PoolUpdate { - address token; // The IERC20 token address - address pool; // The token pool address - } - struct SourceTokenData { // The source pool address, abi encoded. This value is trusted as it was obtained through the onRamp. It can be // relied upon by the destination pool to validate the source pool. @@ -72,7 +66,7 @@ library Internal { /// @notice Report that is submitted by the execution DON at the execution phase. (including chain selector data) /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers. - struct ExecutionReportSingleChain { + struct ExecutionReport { uint64 sourceChainSelector; // Source chain selector for which the report is submitted Any2EVMRampMessage[] messages; // Contains a bytes array for each message, each inner bytes array contains bytes per transferred token @@ -81,63 +75,13 @@ library Internal { uint256 proofFlagBits; } - /// @notice Report that is submitted by the execution DON at the execution phase. - /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers. - struct ExecutionReport { - EVM2EVMMessage[] messages; - // Contains a bytes array for each message, each inner bytes array contains bytes per transferred token - bytes[][] offchainTokenData; - bytes32[] proofs; - uint256 proofFlagBits; - } - - /// @notice The cross chain message that gets committed to EVM chains. - /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers. - struct EVM2EVMMessage { - uint64 sourceChainSelector; // ────────╮ the chain selector of the source chain, note: not chainId - address sender; // ────────────────────╯ sender address on the source chain - address receiver; // ──────────────────╮ receiver address on the destination chain - uint64 sequenceNumber; // ─────────────╯ sequence number, not unique across lanes - uint256 gasLimit; // user supplied maximum gas amount available for dest chain execution - bool strict; // ───────────────────────╮ DEPRECATED - uint64 nonce; // │ nonce for this lane for this sender, not unique across senders/lanes - address feeToken; // ──────────────────╯ fee token - uint256 feeTokenAmount; // fee token amount - bytes data; // arbitrary data payload supplied by the message sender - Client.EVMTokenAmount[] tokenAmounts; // array of tokens and amounts to transfer - bytes[] sourceTokenData; // array of token data, one per token - bytes32 messageId; // a hash of the message data - } - - /// @dev EVM2EVMMessage struct has 13 fields, including 3 variable arrays. - /// Each variable array takes 1 more slot to store its length. - /// When abi encoded, excluding array contents, - /// EVM2EVMMessage takes up a fixed number of 16 lots, 32 bytes each. - /// For structs that contain arrays, 1 more slot is added to the front, reaching a total of 17. - uint256 public constant MESSAGE_FIXED_BYTES = 32 * 17; - - /// @dev Each token transfer adds 1 EVMTokenAmount and 3 bytes at 3 slots each and one slot for the destGasAmount. - /// When abi encoded, each EVMTokenAmount takes 2 slots, each bytes takes 1 slot for length, one slot of data and one - /// slot for the offset. This results in effectively 3*3 slots per SourceTokenData. - /// 0x20 - /// destGasAmount - /// sourcePoolAddress_offset - /// destTokenAddress_offset - /// extraData_offset - /// sourcePoolAddress_length - /// sourcePoolAddress_content // assume 1 slot - /// destTokenAddress_length - /// destTokenAddress_content // assume 1 slot - /// extraData_length // contents billed separately - uint256 public constant MESSAGE_FIXED_BYTES_PER_TOKEN = 32 * ((1 + 3 * 3) + 2); - /// @dev Any2EVMRampMessage struct has 10 fields, including 3 variable unnested arrays (data, receiver and tokenAmounts). /// Each variable array takes 1 more slot to store its length. /// When abi encoded, excluding array contents, /// Any2EVMMessage takes up a fixed number of 13 slots, 32 bytes each. /// For structs that contain arrays, 1 more slot is added to the front, reaching a total of 14. - /// The fixed bytes does not cover struct data (this is represented by ANY_2_EVM_MESSAGE_FIXED_BYTES_PER_TOKEN) - uint256 public constant ANY_2_EVM_MESSAGE_FIXED_BYTES = 32 * 14; + /// The fixed bytes does not cover struct data (this is represented by MESSAGE_FIXED_BYTES_PER_TOKEN) + uint256 public constant MESSAGE_FIXED_BYTES = 32 * 14; /// @dev Each token transfer adds 1 RampTokenAmount /// RampTokenAmount has 5 fields, 2 of which are bytes type, 1 Address, 1 uint256 and 1 uint32. @@ -145,41 +89,7 @@ library Internal { /// address /// uint256 amount takes 1 slot. /// uint32 destGasAmount takes 1 slot. - uint256 public constant ANY_2_EVM_MESSAGE_FIXED_BYTES_PER_TOKEN = 32 * ((2 * 3) + 3); - - bytes32 internal constant EVM_2_EVM_MESSAGE_HASH = keccak256("EVM2EVMMessageHashV2"); - - /// @dev Used to hash messages for single-lane ramps. - /// OnRamp hash(EVM2EVMMessage) = OffRamp hash(EVM2EVMMessage) - /// The EVM2EVMMessage's messageId is expected to be the output of this hash function - /// @param original Message to hash - /// @param metadataHash Immutable metadata hash representing a lane with a fixed OnRamp - /// @return hashedMessage hashed message as a keccak256 - function _hash(EVM2EVMMessage memory original, bytes32 metadataHash) internal pure returns (bytes32) { - // Fixed-size message fields are included in nested hash to reduce stack pressure. - // This hashing scheme is also used by RMN. If changing it, please notify the RMN maintainers. - return keccak256( - abi.encode( - MerkleMultiProof.LEAF_DOMAIN_SEPARATOR, - metadataHash, - keccak256( - abi.encode( - original.sender, - original.receiver, - original.sequenceNumber, - original.gasLimit, - original.strict, - original.nonce, - original.feeToken, - original.feeTokenAmount - ) - ), - keccak256(original.data), - keccak256(abi.encode(original.tokenAmounts)), - keccak256(abi.encode(original.sourceTokenData)) - ) - ); - } + uint256 public constant MESSAGE_FIXED_BYTES_PER_TOKEN = 32 * ((2 * 3) + 3); bytes32 internal constant ANY_2_EVM_MESSAGE_HASH = keccak256("Any2EVMMessageHashV1"); bytes32 internal constant EVM_2_ANY_MESSAGE_HASH = keccak256("EVM2AnyMessageHashV1"); @@ -283,7 +193,7 @@ library Internal { struct RampMessageHeader { bytes32 messageId; // Unique identifier for the message, generated with the source chain's encoding scheme (i.e. not necessarily abi.encoded) uint64 sourceChainSelector; // ──╮ the chain selector of the source chain, note: not chainId - uint64 destChainSelector; // | the chain selector of the destination chain, note: not chainId + uint64 destChainSelector; // │ the chain selector of the destination chain, note: not chainId uint64 sequenceNumber; // │ sequence number, not unique across lanes uint64 nonce; // ────────────────╯ nonce for this lane for this sender, not unique across senders/lanes } @@ -351,13 +261,13 @@ library Internal { /// @dev Struct to hold a merkle root and an interval for a source chain so that an array of these can be passed in the CommitReport. /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers. - /// @dev ineffiecient struct packing intentionally chosen to maintain order of specificity. Not a storage struct so impact is minimal. + /// @dev inefficient struct packing intentionally chosen to maintain order of specificity. Not a storage struct so impact is minimal. // solhint-disable-next-line gas-struct-packing struct MerkleRoot { - uint64 sourceChainSelector; // Remote source chain selector that the Merkle Root is scoped to - bytes onRampAddress; // Generic onramp address, to support arbitrary sources; for EVM, use abi.encode - uint64 minSeqNr; // ─────────────╮ Minimum sequence number, inclusive - uint64 maxSeqNr; // ─────────────╯ Maximum sequence number, inclusive - bytes32 merkleRoot; // Merkle root covering the interval & source chain messages + uint64 sourceChainSelector; // Remote source chain selector that the Merkle Root is scoped to + bytes onRampAddress; // Generic onramp address, to support arbitrary sources; for EVM, use abi.encode + uint64 minSeqNr; // ──────────╮ Minimum sequence number, inclusive + uint64 maxSeqNr; // ──────────╯ Maximum sequence number, inclusive + bytes32 merkleRoot; // Merkle root covering the interval & source chain messages } } diff --git a/contracts/src/v0.8/ccip/ocr/OCR2Abstract.sol b/contracts/src/v0.8/ccip/ocr/OCR2Abstract.sol deleted file mode 100644 index 741433bd5ad..00000000000 --- a/contracts/src/v0.8/ccip/ocr/OCR2Abstract.sol +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; - -import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; - -abstract contract OCR2Abstract is ITypeAndVersion { - // Maximum number of oracles the offchain reporting protocol is designed for - uint256 internal constant MAX_NUM_ORACLES = 31; - - /// @notice triggers a new run of the offchain reporting protocol - /// @param previousConfigBlockNumber block in which the previous config was set, to simplify historic analysis - /// @param configDigest configDigest of this configuration - /// @param configCount ordinal number of this config setting among all config settings over the life of this contract - /// @param signers ith element is address ith oracle uses to sign a report - /// @param transmitters ith element is address ith oracle uses to transmit a report via the transmit method - /// @param f maximum number of faulty/dishonest oracles the protocol can tolerate while still working correctly - /// @param onchainConfig serialized configuration used by the contract (and possibly oracles) - /// @param offchainConfigVersion version of the serialization format used for "offchainConfig" parameter - /// @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract - event ConfigSet( - uint32 previousConfigBlockNumber, - bytes32 configDigest, - uint64 configCount, - address[] signers, - address[] transmitters, - uint8 f, - bytes onchainConfig, - uint64 offchainConfigVersion, - bytes offchainConfig - ); - - /// @notice sets offchain reporting protocol configuration incl. participating oracles - /// @param signers addresses with which oracles sign the reports - /// @param transmitters addresses oracles use to transmit the reports - /// @param f number of faulty oracles the system can tolerate - /// @param onchainConfig serialized configuration used by the contract (and possibly oracles) - /// @param offchainConfigVersion version number for offchainEncoding schema - /// @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract - function setOCR2Config( - address[] memory signers, - address[] memory transmitters, - uint8 f, - bytes memory onchainConfig, - uint64 offchainConfigVersion, - bytes memory offchainConfig - ) external virtual; - - /// @notice information about current offchain reporting protocol configuration - /// @return configCount ordinal number of current config, out of all configs applied to this contract so far - /// @return blockNumber block at which this config was set - /// @return configDigest domain-separation tag for current config (see _configDigestFromConfigData) - function latestConfigDetails() - external - view - virtual - returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest); - - function _configDigestFromConfigData( - uint256 chainId, - address contractAddress, - uint64 configCount, - address[] memory signers, - address[] memory transmitters, - uint8 f, - bytes memory onchainConfig, - uint64 offchainConfigVersion, - bytes memory offchainConfig - ) internal pure returns (bytes32) { - uint256 h = uint256( - keccak256( - abi.encode( - chainId, - contractAddress, - configCount, - signers, - transmitters, - f, - onchainConfig, - offchainConfigVersion, - offchainConfig - ) - ) - ); - uint256 prefixMask = type(uint256).max << (256 - 16); // 0xFFFF00..00 - uint256 prefix = 0x0001 << (256 - 16); // 0x000100..00 - return bytes32((prefix & prefixMask) | (h & ~prefixMask)); - } - - /// @notice optionally emitted to indicate the latest configDigest and epoch for - /// which a report was successfully transmitted. Alternatively, the contract may - /// use latestConfigDigestAndEpoch with scanLogs set to false. - event Transmitted(bytes32 configDigest, uint32 epoch); - - /// @notice optionally returns the latest configDigest and epoch for which a - /// report was successfully transmitted. Alternatively, the contract may return - /// scanLogs set to true and use Transmitted events to provide this information - /// to offchain watchers. - /// @return scanLogs indicates whether to rely on the configDigest and epoch - /// returned or whether to scan logs for the Transmitted event instead. - /// @return configDigest - /// @return epoch - function latestConfigDigestAndEpoch() - external - view - virtual - returns (bool scanLogs, bytes32 configDigest, uint32 epoch); - - /// @notice transmit is called to post a new report to the contract - /// @param report serialized report, which the signatures are signing. - /// @param rs ith element is the R components of the ith signature on report. Must have at most MAX_NUM_ORACLES entries - /// @param ss ith element is the S components of the ith signature on report. Must have at most MAX_NUM_ORACLES entries - /// @param rawVs ith element is the the V component of the ith signature - function transmit( - // NOTE: If these parameters are changed, expectedMsgDataLength and/or - // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly - bytes32[3] calldata reportContext, - bytes calldata report, - bytes32[] calldata rs, - bytes32[] calldata ss, - bytes32 rawVs // signatures - ) external virtual; -} diff --git a/contracts/src/v0.8/ccip/ocr/OCR2Base.sol b/contracts/src/v0.8/ccip/ocr/OCR2Base.sol deleted file mode 100644 index 74c8e479173..00000000000 --- a/contracts/src/v0.8/ccip/ocr/OCR2Base.sol +++ /dev/null @@ -1,291 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.4; - -import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol"; -import {OCR2Abstract} from "./OCR2Abstract.sol"; - -/// @notice Onchain verification of reports from the offchain reporting protocol -/// @dev For details on its operation, see the offchain reporting protocol design -/// doc, which refers to this contract as simply the "contract". -abstract contract OCR2Base is OwnerIsCreator, OCR2Abstract { - error InvalidConfig(InvalidConfigErrorType errorType); - error WrongMessageLength(uint256 expected, uint256 actual); - error ConfigDigestMismatch(bytes32 expected, bytes32 actual); - error ForkedChain(uint256 expected, uint256 actual); - error WrongNumberOfSignatures(); - error SignaturesOutOfRegistration(); - error UnauthorizedTransmitter(); - error UnauthorizedSigner(); - error NonUniqueSignatures(); - error OracleCannotBeZeroAddress(); - - enum InvalidConfigErrorType { - F_MUST_BE_POSITIVE, - TOO_MANY_SIGNERS, - F_TOO_HIGH, - REPEATED_ORACLE_ADDRESS, - NUM_SIGNERS_NOT_NUM_TRANSMITTERS - } - - // Packing these fields used on the hot path in a ConfigInfo variable reduces the - // retrieval of all of them to a minimum number of SLOADs. - struct ConfigInfo { - bytes32 latestConfigDigest; - uint8 f; - uint8 n; - } - - // Used for s_oracles[a].role, where a is an address, to track the purpose - // of the address, or to indicate that the address is unset. - enum Role { - // No oracle role has been set for address a - Unset, - // Signing address for the s_oracles[a].index'th oracle. I.e., report - // signatures from this oracle should ecrecover back to address a. - Signer, - // Transmission address for the s_oracles[a].index'th oracle. I.e., if a - // report is received by OCR2Aggregator.transmit in which msg.sender is - // a, it is attributed to the s_oracles[a].index'th oracle. - Transmitter - } - - struct Oracle { - uint8 index; // Index of oracle in s_signers/s_transmitters - Role role; // Role of the address which mapped to this struct - } - - // The current config - ConfigInfo internal s_configInfo; - - // incremented each time a new config is posted. This count is incorporated - // into the config digest, to prevent replay attacks. - uint32 internal s_configCount; - // makes it easier for offchain systems to extract config from logs. - uint32 internal s_latestConfigBlockNumber; - - // signer OR transmitter address - mapping(address signerOrTransmitter => Oracle oracle) internal s_oracles; - - // s_signers contains the signing address of each oracle - address[] internal s_signers; - - // s_transmitters contains the transmission address of each oracle, - // i.e. the address the oracle actually sends transactions to the contract from - address[] internal s_transmitters; - - // The constant-length components of the msg.data sent to transmit. - // See the "If we wanted to call sam" example on for example reasoning - // https://solidity.readthedocs.io/en/v0.7.2/abi-spec.html - uint16 private constant TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT = 4 // function selector - + 32 * 3 // 3 words containing reportContext - + 32 // word containing start location of abiencoded report value - + 32 // word containing location start of abiencoded rs value - + 32 // word containing start location of abiencoded ss value - + 32 // rawVs value - + 32 // word containing length of report - + 32 // word containing length rs - + 32; // word containing length of ss - - bool internal immutable i_uniqueReports; - uint256 internal immutable i_chainID; - - constructor(bool uniqueReports) { - i_uniqueReports = uniqueReports; - i_chainID = block.chainid; - } - - // Reverts transaction if config args are invalid - modifier checkConfigValid(uint256 numSigners, uint256 numTransmitters, uint256 f) { - if (numSigners > MAX_NUM_ORACLES) revert InvalidConfig(InvalidConfigErrorType.TOO_MANY_SIGNERS); - if (f == 0) revert InvalidConfig(InvalidConfigErrorType.F_MUST_BE_POSITIVE); - if (numSigners != numTransmitters) revert InvalidConfig(InvalidConfigErrorType.NUM_SIGNERS_NOT_NUM_TRANSMITTERS); - if (numSigners <= 3 * f) revert InvalidConfig(InvalidConfigErrorType.F_TOO_HIGH); - _; - } - - /// @notice sets offchain reporting protocol configuration incl. participating oracles - /// @param signers addresses with which oracles sign the reports - /// @param transmitters addresses oracles use to transmit the reports - /// @param f number of faulty oracles the system can tolerate - /// @param onchainConfig encoded on-chain contract configuration - /// @param offchainConfigVersion version number for offchainEncoding schema - /// @param offchainConfig encoded off-chain oracle configuration - function setOCR2Config( - address[] memory signers, - address[] memory transmitters, - uint8 f, - bytes memory onchainConfig, - uint64 offchainConfigVersion, - bytes memory offchainConfig - ) external override checkConfigValid(signers.length, transmitters.length, f) onlyOwner { - _beforeSetConfig(onchainConfig); - uint256 oldSignerLength = s_signers.length; - for (uint256 i = 0; i < oldSignerLength; ++i) { - delete s_oracles[s_signers[i]]; - delete s_oracles[s_transmitters[i]]; - } - - uint256 newSignersLength = signers.length; - for (uint256 i = 0; i < newSignersLength; ++i) { - // add new signer/transmitter addresses - address signer = signers[i]; - if (s_oracles[signer].role != Role.Unset) revert InvalidConfig(InvalidConfigErrorType.REPEATED_ORACLE_ADDRESS); - if (signer == address(0)) revert OracleCannotBeZeroAddress(); - s_oracles[signer] = Oracle(uint8(i), Role.Signer); - - address transmitter = transmitters[i]; - if (s_oracles[transmitter].role != Role.Unset) { - revert InvalidConfig(InvalidConfigErrorType.REPEATED_ORACLE_ADDRESS); - } - if (transmitter == address(0)) revert OracleCannotBeZeroAddress(); - s_oracles[transmitter] = Oracle(uint8(i), Role.Transmitter); - } - - s_signers = signers; - s_transmitters = transmitters; - - s_configInfo.f = f; - s_configInfo.n = uint8(newSignersLength); - s_configInfo.latestConfigDigest = _configDigestFromConfigData( - block.chainid, - address(this), - ++s_configCount, - signers, - transmitters, - f, - onchainConfig, - offchainConfigVersion, - offchainConfig - ); - - uint32 previousConfigBlockNumber = s_latestConfigBlockNumber; - s_latestConfigBlockNumber = uint32(block.number); - - emit ConfigSet( - previousConfigBlockNumber, - s_configInfo.latestConfigDigest, - s_configCount, - signers, - transmitters, - f, - onchainConfig, - offchainConfigVersion, - offchainConfig - ); - } - - /// @dev Hook that is run from setOCR2Config() right after validating configuration. - /// Empty by default, please provide an implementation in a child contract if you need additional configuration processing - function _beforeSetConfig(bytes memory _onchainConfig) internal virtual; - - /// @return list of addresses permitted to transmit reports to this contract - /// @dev The list will match the order used to specify the transmitter during setConfig - function getTransmitters() external view returns (address[] memory) { - return s_transmitters; - } - - /// @notice transmit is called to post a new report to the contract - /// @param report serialized report, which the signatures are signing. - /// @param rs ith element is the R components of the ith signature on report. Must have at most MAX_NUM_ORACLES entries - /// @param ss ith element is the S components of the ith signature on report. Must have at most MAX_NUM_ORACLES entries - /// @param rawVs ith element is the the V component of the ith signature - function transmit( - // NOTE: If these parameters are changed, expectedMsgDataLength and/or - // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly - bytes32[3] calldata reportContext, - bytes calldata report, - bytes32[] calldata rs, - bytes32[] calldata ss, - bytes32 rawVs // signatures - ) external override { - // Scoping this reduces stack pressure and gas usage - { - // report and epochAndRound - _report(report, uint40(uint256(reportContext[1]))); - } - - // reportContext consists of: - // reportContext[0]: ConfigDigest - // reportContext[1]: 27 byte padding, 4-byte epoch and 1-byte round - // reportContext[2]: ExtraHash - bytes32 configDigest = reportContext[0]; - ConfigInfo memory configInfo = s_configInfo; - - if (configInfo.latestConfigDigest != configDigest) { - revert ConfigDigestMismatch(configInfo.latestConfigDigest, configDigest); - } - // If the cached chainID at time of deployment doesn't match the current chainID, we reject all signed reports. - // This avoids a (rare) scenario where chain A forks into chain A and A', A' still has configDigest - // calculated from chain A and so OCR reports will be valid on both forks. - if (i_chainID != block.chainid) revert ForkedChain(i_chainID, block.chainid); - - emit Transmitted(configDigest, uint32(uint256(reportContext[1]) >> 8)); - - uint256 expectedNumSignatures; - if (i_uniqueReports) { - expectedNumSignatures = (configInfo.n + configInfo.f) / 2 + 1; - } else { - expectedNumSignatures = configInfo.f + 1; - } - if (rs.length != expectedNumSignatures) revert WrongNumberOfSignatures(); - if (rs.length != ss.length) revert SignaturesOutOfRegistration(); - - // Scoping this reduces stack pressure and gas usage - { - Oracle memory transmitter = s_oracles[msg.sender]; - // Check that sender is authorized to report - if (!(transmitter.role == Role.Transmitter && msg.sender == s_transmitters[transmitter.index])) { - revert UnauthorizedTransmitter(); - } - } - // Scoping this reduces stack pressure and gas usage - { - uint256 expectedDataLength = uint256(TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT) + report.length // one byte pure entry in _report - + rs.length * 32 // 32 bytes per entry in _rs - + ss.length * 32; // 32 bytes per entry in _ss) - if (msg.data.length != expectedDataLength) revert WrongMessageLength(expectedDataLength, msg.data.length); - } - - // Verify signatures attached to report - bytes32 h = keccak256(abi.encodePacked(keccak256(report), reportContext)); - bool[MAX_NUM_ORACLES] memory signed; - - uint256 numberOfSignatures = rs.length; - for (uint256 i = 0; i < numberOfSignatures; ++i) { - // Safe from ECDSA malleability here since we check for duplicate signers. - address signer = ecrecover(h, uint8(rawVs[i]) + 27, rs[i], ss[i]); - // Since we disallow address(0) as a valid signer address, it can - // never have a signer role. - Oracle memory oracle = s_oracles[signer]; - if (oracle.role != Role.Signer) revert UnauthorizedSigner(); - if (signed[oracle.index]) revert NonUniqueSignatures(); - signed[oracle.index] = true; - } - } - - /// @notice information about current offchain reporting protocol configuration - /// @return configCount ordinal number of current config, out of all configs applied to this contract so far - /// @return blockNumber block at which this config was set - /// @return configDigest domain-separation tag for current config (see _configDigestFromConfigData) - function latestConfigDetails() - external - view - override - returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest) - { - return (s_configCount, s_latestConfigBlockNumber, s_configInfo.latestConfigDigest); - } - - /// @inheritdoc OCR2Abstract - function latestConfigDigestAndEpoch() - external - view - virtual - override - returns (bool scanLogs, bytes32 configDigest, uint32 epoch) - { - return (true, bytes32(0), uint32(0)); - } - - function _report(bytes calldata report, uint40 epochAndRound) internal virtual; -} diff --git a/contracts/src/v0.8/ccip/ocr/OCR2BaseNoChecks.sol b/contracts/src/v0.8/ccip/ocr/OCR2BaseNoChecks.sol deleted file mode 100644 index 8bada2c378e..00000000000 --- a/contracts/src/v0.8/ccip/ocr/OCR2BaseNoChecks.sol +++ /dev/null @@ -1,242 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.4; - -import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol"; -import {OCR2Abstract} from "./OCR2Abstract.sol"; - -/// @notice Onchain verification of reports from the offchain reporting protocol -/// @dev For details on its operation, see the offchain reporting protocol design -/// doc, which refers to this contract as simply the "contract". -/// @dev This contract does ***NOT*** check the supplied signatures on `transmit` -/// This is intentional. -abstract contract OCR2BaseNoChecks is OwnerIsCreator, OCR2Abstract { - error InvalidConfig(InvalidConfigErrorType errorType); - error WrongMessageLength(uint256 expected, uint256 actual); - error ConfigDigestMismatch(bytes32 expected, bytes32 actual); - error ForkedChain(uint256 expected, uint256 actual); - error UnauthorizedTransmitter(); - error OracleCannotBeZeroAddress(); - - enum InvalidConfigErrorType { - F_MUST_BE_POSITIVE, - TOO_MANY_TRANSMITTERS, - REPEATED_ORACLE_ADDRESS - } - - // Packing these fields used on the hot path in a ConfigInfo variable reduces the - // retrieval of all of them to a minimum number of SLOADs. - struct ConfigInfo { - bytes32 latestConfigDigest; - uint8 f; - uint8 n; - } - - // Used for s_oracles[a].role, where a is an address, to track the purpose - // of the address, or to indicate that the address is unset. - enum Role { - // No oracle role has been set for address a - Unset, - // Unused - Signer, - // Transmission address for the s_oracles[a].index'th oracle. I.e., if a - // report is received by OCR2Aggregator.transmit in which msg.sender is - // a, it is attributed to the s_oracles[a].index'th oracle. - Transmitter - } - - struct Oracle { - uint8 index; // Index of oracle in s_transmitters - Role role; // Role of the address which mapped to this struct - } - - // The current config - ConfigInfo internal s_configInfo; - - // incremented each time a new config is posted. This count is incorporated - // into the config digest, to prevent replay attacks. - uint32 internal s_configCount; - // makes it easier for offchain systems to extract config from logs. - uint32 internal s_latestConfigBlockNumber; - - // Transmitter address - mapping(address transmitter => Oracle oracle) internal s_oracles; - - // s_transmitters contains the transmission address of each oracle, - // i.e. the address the oracle actually sends transactions to the contract from - address[] internal s_transmitters; - - // The constant-length components of the msg.data sent to transmit. - // See the "If we wanted to call sam" example on for example reasoning - // https://solidity.readthedocs.io/en/v0.7.2/abi-spec.html - uint16 private constant TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT = 4 // function selector - + 32 * 3 // 3 words containing reportContext - + 32 // word containing start location of abiencoded report value - + 32 // word containing location start of abiencoded rs value - + 32 // word containing start location of abiencoded ss value - + 32 // rawVs value - + 32 // word containing length of report - + 32 // word containing length rs - + 32; // word containing length of ss - - uint256 internal immutable i_chainID; - - // Reverts transaction if config args are invalid - modifier checkConfigValid(uint256 numTransmitters, uint256 f) { - if (numTransmitters > MAX_NUM_ORACLES) revert InvalidConfig(InvalidConfigErrorType.TOO_MANY_TRANSMITTERS); - if (f == 0) revert InvalidConfig(InvalidConfigErrorType.F_MUST_BE_POSITIVE); - _; - } - - constructor() { - i_chainID = block.chainid; - } - - /// @notice sets offchain reporting protocol configuration incl. participating oracles - /// @param signers addresses with which oracles sign the reports - /// @param transmitters addresses oracles use to transmit the reports - /// @param f number of faulty oracles the system can tolerate - /// @param onchainConfig encoded on-chain contract configuration - /// @param offchainConfigVersion version number for offchainEncoding schema - /// @param offchainConfig encoded off-chain oracle configuration - function setOCR2Config( - address[] memory signers, - address[] memory transmitters, - uint8 f, - bytes memory onchainConfig, - uint64 offchainConfigVersion, - bytes memory offchainConfig - ) external override checkConfigValid(transmitters.length, f) onlyOwner { - _beforeSetConfig(onchainConfig); - // Scoped to reduce contract size - { - uint256 oldTransmitterLength = s_transmitters.length; - for (uint256 i = 0; i < oldTransmitterLength; ++i) { - delete s_oracles[s_transmitters[i]]; - } - } - uint256 newTransmitterLength = transmitters.length; - for (uint256 i = 0; i < newTransmitterLength; ++i) { - address transmitter = transmitters[i]; - if (s_oracles[transmitter].role != Role.Unset) { - revert InvalidConfig(InvalidConfigErrorType.REPEATED_ORACLE_ADDRESS); - } - if (transmitter == address(0)) revert OracleCannotBeZeroAddress(); - s_oracles[transmitter] = Oracle(uint8(i), Role.Transmitter); - } - - s_transmitters = transmitters; - - s_configInfo.f = f; - s_configInfo.n = uint8(newTransmitterLength); - s_configInfo.latestConfigDigest = _configDigestFromConfigData( - block.chainid, - address(this), - ++s_configCount, - signers, - transmitters, - f, - onchainConfig, - offchainConfigVersion, - offchainConfig - ); - - uint32 previousConfigBlockNumber = s_latestConfigBlockNumber; - s_latestConfigBlockNumber = uint32(block.number); - - emit ConfigSet( - previousConfigBlockNumber, - s_configInfo.latestConfigDigest, - s_configCount, - signers, - transmitters, - f, - onchainConfig, - offchainConfigVersion, - offchainConfig - ); - } - - /// @dev Hook that is run from setOCR2Config() right after validating configuration. - /// Empty by default, please provide an implementation in a child contract if you need additional configuration processing - function _beforeSetConfig(bytes memory _onchainConfig) internal virtual; - - /// @return list of addresses permitted to transmit reports to this contract - /// @dev The list will match the order used to specify the transmitter during setConfig - function getTransmitters() external view returns (address[] memory) { - return s_transmitters; - } - - /// @notice transmit is called to post a new report to the contract - /// @param report serialized report, which the signatures are signing. - /// @param rs ith element is the R components of the ith signature on report. Must have at most MAX_NUM_ORACLES entries - /// @param ss ith element is the S components of the ith signature on report. Must have at most MAX_NUM_ORACLES entries - function transmit( - // NOTE: If these parameters are changed, expectedMsgDataLength and/or - // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly - bytes32[3] calldata reportContext, - bytes calldata report, - bytes32[] calldata rs, - bytes32[] calldata ss, - bytes32 // signatures - ) external override { - _report(report); - - // reportContext consists of: - // reportContext[0]: ConfigDigest - // reportContext[1]: 27 byte padding, 4-byte epoch and 1-byte round - // reportContext[2]: ExtraHash - bytes32 configDigest = reportContext[0]; - bytes32 latestConfigDigest = s_configInfo.latestConfigDigest; - if (latestConfigDigest != configDigest) revert ConfigDigestMismatch(latestConfigDigest, configDigest); - _checkChainForked(); - - emit Transmitted(configDigest, uint32(uint256(reportContext[1]) >> 8)); - - // Scoping this reduces stack pressure and gas usage - { - Oracle memory transmitter = s_oracles[msg.sender]; - // Check that sender is authorized to report - if (!(transmitter.role == Role.Transmitter && msg.sender == s_transmitters[transmitter.index])) { - revert UnauthorizedTransmitter(); - } - } - - uint256 expectedDataLength = uint256(TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT) + report.length // one byte pure entry in _report - + rs.length * 32 // 32 bytes per entry in _rs - + ss.length * 32; // 32 bytes per entry in _ss) - if (msg.data.length != expectedDataLength) revert WrongMessageLength(expectedDataLength, msg.data.length); - } - - function _checkChainForked() internal view { - // If the cached chainID at time of deployment doesn't match the current chainID, we reject all signed reports. - // This avoids a (rare) scenario where chain A forks into chain A and A', A' still has configDigest - // calculated from chain A and so OCR reports will be valid on both forks. - if (i_chainID != block.chainid) revert ForkedChain(i_chainID, block.chainid); - } - - /// @notice information about current offchain reporting protocol configuration - /// @return configCount ordinal number of current config, out of all configs applied to this contract so far - /// @return blockNumber block at which this config was set - /// @return configDigest domain-separation tag for current config (see _configDigestFromConfigData) - function latestConfigDetails() - external - view - override - returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest) - { - return (s_configCount, s_latestConfigBlockNumber, s_configInfo.latestConfigDigest); - } - - /// @inheritdoc OCR2Abstract - function latestConfigDigestAndEpoch() - external - view - virtual - override - returns (bool scanLogs, bytes32 configDigest, uint32 epoch) - { - return (true, bytes32(0), uint32(0)); - } - - function _report(bytes calldata report) internal virtual; -} diff --git a/contracts/src/v0.8/ccip/offRamp/EVM2EVMOffRamp.sol b/contracts/src/v0.8/ccip/offRamp/EVM2EVMOffRamp.sol deleted file mode 100644 index 0be7fe75119..00000000000 --- a/contracts/src/v0.8/ccip/offRamp/EVM2EVMOffRamp.sol +++ /dev/null @@ -1,796 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; -import {IAny2EVMMessageReceiver} from "../interfaces/IAny2EVMMessageReceiver.sol"; -import {IAny2EVMOffRamp} from "../interfaces/IAny2EVMOffRamp.sol"; -import {ICommitStore} from "../interfaces/ICommitStore.sol"; -import {IPoolV1} from "../interfaces/IPool.sol"; -import {IPriceRegistry} from "../interfaces/IPriceRegistry.sol"; -import {IRMN} from "../interfaces/IRMN.sol"; -import {IRouter} from "../interfaces/IRouter.sol"; -import {ITokenAdminRegistry} from "../interfaces/ITokenAdminRegistry.sol"; - -import {CallWithExactGas} from "../../shared/call/CallWithExactGas.sol"; -import {EnumerableMapAddresses} from "../../shared/enumerable/EnumerableMapAddresses.sol"; -import {AggregateRateLimiter} from "../AggregateRateLimiter.sol"; -import {Client} from "../libraries/Client.sol"; -import {Internal} from "../libraries/Internal.sol"; -import {Pool} from "../libraries/Pool.sol"; -import {RateLimiter} from "../libraries/RateLimiter.sol"; -import {OCR2BaseNoChecks} from "../ocr/OCR2BaseNoChecks.sol"; - -import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {ERC165Checker} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/introspection/ERC165Checker.sol"; - -/// @notice EVM2EVMOffRamp enables OCR networks to execute multiple messages -/// in an OffRamp in a single transaction. -/// @dev The EVM2EVMOnRamp, CommitStore and EVM2EVMOffRamp form an xchain upgradeable unit. Any change to one of them -/// results an onchain upgrade of all 3. -/// @dev OCR2BaseNoChecks is used to save gas, signatures are not required as the offramp can only execute -/// messages which are committed in the commitStore. We still make use of OCR2 as an executor whitelist -/// and turn-taking mechanism. -contract EVM2EVMOffRamp is IAny2EVMOffRamp, AggregateRateLimiter, ITypeAndVersion, OCR2BaseNoChecks { - using ERC165Checker for address; - using EnumerableMapAddresses for EnumerableMapAddresses.AddressToAddressMap; - - error ZeroAddressNotAllowed(); - error CommitStoreAlreadyInUse(); - error ExecutionError(bytes err); - error InvalidSourceChain(uint64 sourceChainSelector); - error MessageTooLarge(uint256 maxSize, uint256 actualSize); - error TokenDataMismatch(uint64 sequenceNumber); - error UnexpectedTokenData(); - error UnsupportedNumberOfTokens(uint64 sequenceNumber); - error ManualExecutionNotYetEnabled(); - error ManualExecutionGasLimitMismatch(); - error DestinationGasAmountCountMismatch(bytes32 messageId, uint64 sequenceNumber); - error InvalidManualExecutionGasLimit(bytes32 messageId, uint256 oldLimit, uint256 newLimit); - error InvalidTokenGasOverride(bytes32 messageId, uint256 tokenIndex, uint256 oldLimit, uint256 tokenGasOverride); - error RootNotCommitted(); - error CanOnlySelfCall(); - error ReceiverError(bytes err); - error TokenHandlingError(bytes err); - error ReleaseOrMintBalanceMismatch(uint256 amountReleased, uint256 balancePre, uint256 balancePost); - error EmptyReport(); - error CursedByRMN(); - error InvalidMessageId(); - error NotACompatiblePool(address notPool); - error InvalidDataLength(uint256 expected, uint256 got); - error InvalidNewState(uint64 sequenceNumber, Internal.MessageExecutionState newState); - - /// @dev Atlas depends on this event, if changing, please notify Atlas. - event ConfigSet(StaticConfig staticConfig, DynamicConfig dynamicConfig); - event SkippedIncorrectNonce(uint64 indexed nonce, address indexed sender); - event SkippedSenderWithPreviousRampMessageInflight(uint64 indexed nonce, address indexed sender); - /// @dev RMN depends on this event, if changing, please notify the RMN maintainers. - event ExecutionStateChanged( - uint64 indexed sequenceNumber, bytes32 indexed messageId, Internal.MessageExecutionState state, bytes returnData - ); - event TokenAggregateRateLimitAdded(address sourceToken, address destToken); - event TokenAggregateRateLimitRemoved(address sourceToken, address destToken); - event SkippedAlreadyExecutedMessage(uint64 indexed sequenceNumber); - event AlreadyAttempted(uint64 sequenceNumber); - - /// @notice Static offRamp config - /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers. - //solhint-disable gas-struct-packing - struct StaticConfig { - address commitStore; // ────────╮ CommitStore address on the destination chain - uint64 chainSelector; // ───────╯ Destination chainSelector - uint64 sourceChainSelector; // ─╮ Source chainSelector - address onRamp; // ─────────────╯ OnRamp address on the source chain - address prevOffRamp; // Address of previous-version OffRamp - address rmnProxy; // RMN proxy address - address tokenAdminRegistry; // Token admin registry address - } - - /// @notice Dynamic offRamp config - /// @dev since OffRampConfig is part of OffRampConfigChanged event, if changing it, we should update the ABI on Atlas - struct DynamicConfig { - uint32 permissionLessExecutionThresholdSeconds; // ─╮ Waiting time before manual execution is enabled - uint32 maxDataBytes; // │ Maximum payload data size in bytes - uint16 maxNumberOfTokensPerMsg; // │ Maximum number of ERC20 token transfers that can be included per message - address router; // ─────────────────────────────────╯ Router address - address priceRegistry; // Price registry address - } - - /// @notice RateLimitToken struct containing both the source and destination token addresses - struct RateLimitToken { - address sourceToken; - address destToken; - } - - /// @notice Gas overrides for manual exec, the number of token overrides must match the number of tokens in the msg. - struct GasLimitOverride { - /// @notice Overrides EVM2EVMMessage.gasLimit. A value of zero indicates no override and is valid. - uint256 receiverExecutionGasLimit; - /// @notice Overrides EVM2EVMMessage.sourceTokenData.destGasAmount. Must be same length as tokenAmounts. A value - /// of zero indicates no override and is valid. - uint32[] tokenGasOverrides; - } - - // STATIC CONFIG - string public constant override typeAndVersion = "EVM2EVMOffRamp 1.5.0"; - - /// @dev Commit store address on the destination chain - address internal immutable i_commitStore; - /// @dev ChainSelector of the source chain - uint64 internal immutable i_sourceChainSelector; - /// @dev ChainSelector of this chain - uint64 internal immutable i_chainSelector; - /// @dev OnRamp address on the source chain - address internal immutable i_onRamp; - /// @dev metadataHash is a lane-specific prefix for a message hash preimage which ensures global uniqueness. - /// Ensures that 2 identical messages sent to 2 different lanes will have a distinct hash. - /// Must match the metadataHash used in computing leaf hashes offchain for the root committed in - /// the commitStore and i_metadataHash in the onRamp. - bytes32 internal immutable i_metadataHash; - /// @dev The address of previous-version OffRamp for this lane. - /// Used to be able to provide sequencing continuity during a zero downtime upgrade. - address internal immutable i_prevOffRamp; - /// @dev The address of the RMN proxy - address internal immutable i_rmnProxy; - /// @dev The address of the token admin registry - address internal immutable i_tokenAdminRegistry; - - // DYNAMIC CONFIG - DynamicConfig internal s_dynamicConfig; - /// @dev Tokens that should be included in Aggregate Rate Limiting - /// An (address => address) map is used for backwards compatability of offchain code - EnumerableMapAddresses.AddressToAddressMap internal s_rateLimitedTokensDestToSource; - - // STATE - /// @dev The expected nonce for a given sender. - /// Corresponds to s_senderNonce in the OnRamp, used to enforce that messages are - /// executed in the same order they are sent (assuming they are DON). Note that re-execution - /// of FAILED messages however, can be out of order. - mapping(address sender => uint64 nonce) internal s_senderNonce; - /// @dev A mapping of sequence numbers to execution state using a bitmap with each execution - /// state only taking up 2 bits of the uint256, packing 128 states into a single slot. - /// Message state is tracked to ensure message can only be executed successfully once. - mapping(uint64 seqNum => uint256 executionStateBitmap) internal s_executionStates; - - constructor( - StaticConfig memory staticConfig, - RateLimiter.Config memory rateLimiterConfig - ) OCR2BaseNoChecks() AggregateRateLimiter(rateLimiterConfig) { - if ( - staticConfig.onRamp == address(0) || staticConfig.commitStore == address(0) - || staticConfig.tokenAdminRegistry == address(0) - ) revert ZeroAddressNotAllowed(); - // Ensures we can never deploy a new offRamp that points to a commitStore that - // already has roots committed. - if (ICommitStore(staticConfig.commitStore).getExpectedNextSequenceNumber() != 1) revert CommitStoreAlreadyInUse(); - - i_commitStore = staticConfig.commitStore; - i_sourceChainSelector = staticConfig.sourceChainSelector; - i_chainSelector = staticConfig.chainSelector; - i_onRamp = staticConfig.onRamp; - i_prevOffRamp = staticConfig.prevOffRamp; - i_rmnProxy = staticConfig.rmnProxy; - i_tokenAdminRegistry = staticConfig.tokenAdminRegistry; - - i_metadataHash = _metadataHash(Internal.EVM_2_EVM_MESSAGE_HASH); - } - - // ================================================================ - // │ Messaging │ - // ================================================================ - - // The size of the execution state in bits - uint256 private constant MESSAGE_EXECUTION_STATE_BIT_WIDTH = 2; - // The mask for the execution state bits - uint256 private constant MESSAGE_EXECUTION_STATE_MASK = (1 << MESSAGE_EXECUTION_STATE_BIT_WIDTH) - 1; - - /// @notice Returns the current execution state of a message based on its sequenceNumber. - /// @param sequenceNumber The sequence number of the message to get the execution state for. - /// @return The current execution state of the message. - /// @dev we use the literal number 128 because using a constant increased gas usage. - function getExecutionState(uint64 sequenceNumber) public view returns (Internal.MessageExecutionState) { - return Internal.MessageExecutionState( - (s_executionStates[sequenceNumber / 128] >> ((sequenceNumber % 128) * MESSAGE_EXECUTION_STATE_BIT_WIDTH)) - & MESSAGE_EXECUTION_STATE_MASK - ); - } - - /// @notice Sets a new execution state for a given sequence number. It will overwrite any existing state. - /// @param sequenceNumber The sequence number for which the state will be saved. - /// @param newState The new value the state will be in after this function is called. - /// @dev we use the literal number 128 because using a constant increased gas usage. - function _setExecutionState(uint64 sequenceNumber, Internal.MessageExecutionState newState) internal { - uint256 offset = (sequenceNumber % 128) * MESSAGE_EXECUTION_STATE_BIT_WIDTH; - uint256 bitmap = s_executionStates[sequenceNumber / 128]; - // to unset any potential existing state we zero the bits of the section the state occupies, - // then we do an AND operation to blank out any existing state for the section. - bitmap &= ~(MESSAGE_EXECUTION_STATE_MASK << offset); - // Set the new state - bitmap |= uint256(newState) << offset; - - s_executionStates[sequenceNumber / 128] = bitmap; - } - - /// @inheritdoc IAny2EVMOffRamp - function getSenderNonce(address sender) external view returns (uint64 nonce) { - uint256 senderNonce = s_senderNonce[sender]; - - if (senderNonce == 0) { - if (i_prevOffRamp != address(0)) { - // If OffRamp was upgraded, check if sender has a nonce from the previous OffRamp. - return IAny2EVMOffRamp(i_prevOffRamp).getSenderNonce(sender); - } - } - return uint64(senderNonce); - } - - /// @notice Manually execute a message. - /// @param report Internal.ExecutionReport. - /// @param gasLimitOverrides New gasLimit for each message in the report. - /// @dev We permit gas limit overrides so that users may manually execute messages which failed due to - /// insufficient gas provided. - function manuallyExecute( - Internal.ExecutionReport memory report, - GasLimitOverride[] memory gasLimitOverrides - ) external { - // We do this here because the other _execute path is already covered OCR2BaseXXX. - _checkChainForked(); - - uint256 numMsgs = report.messages.length; - if (numMsgs != gasLimitOverrides.length) revert ManualExecutionGasLimitMismatch(); - for (uint256 i = 0; i < numMsgs; ++i) { - Internal.EVM2EVMMessage memory message = report.messages[i]; - GasLimitOverride memory gasLimitOverride = gasLimitOverrides[i]; - - uint256 newLimit = gasLimitOverride.receiverExecutionGasLimit; - // Checks to ensure message cannot be executed with less gas than specified. - if (newLimit != 0) { - if (newLimit < message.gasLimit) { - revert InvalidManualExecutionGasLimit(message.messageId, message.gasLimit, newLimit); - } - } - - if (message.tokenAmounts.length != gasLimitOverride.tokenGasOverrides.length) { - revert DestinationGasAmountCountMismatch(message.messageId, message.sequenceNumber); - } - - bytes[] memory encodedSourceTokenData = message.sourceTokenData; - - for (uint256 j = 0; j < message.tokenAmounts.length; ++j) { - Internal.SourceTokenData memory sourceTokenData = - abi.decode(encodedSourceTokenData[i], (Internal.SourceTokenData)); - uint256 tokenGasOverride = gasLimitOverride.tokenGasOverrides[j]; - - // The gas limit can not be lowered as that could cause the message to fail. If manual execution is done - // from an UNTOUCHED state and we would allow lower gas limit, anyone could grief by executing the message with - // lower gas limit than the DON would have used. This results in the message being marked FAILURE and the DON - // would not attempt it with the correct gas limit. - if (tokenGasOverride != 0 && tokenGasOverride < sourceTokenData.destGasAmount) { - revert InvalidTokenGasOverride(message.messageId, j, sourceTokenData.destGasAmount, tokenGasOverride); - } - } - } - - _execute(report, gasLimitOverrides); - } - - /// @notice Entrypoint for execution, called by the OCR network - /// @dev Expects an encoded ExecutionReport - /// @dev Supplies no GasLimitOverrides as the DON will only execute with the original gas limits. - function _report(bytes calldata report) internal override { - _execute(abi.decode(report, (Internal.ExecutionReport)), new GasLimitOverride[](0)); - } - - /// @notice Executes a report, executing each message in order. - /// @param report The execution report containing the messages and proofs. - /// @param manualExecGasOverrides An array of gas limits to use for manual execution. - /// @dev If called from the DON, this array is always empty. - /// @dev If called from manual execution, this array is always same length as messages. - function _execute(Internal.ExecutionReport memory report, GasLimitOverride[] memory manualExecGasOverrides) internal { - if (IRMN(i_rmnProxy).isCursed(bytes16(uint128(i_sourceChainSelector)))) revert CursedByRMN(); - - uint256 numMsgs = report.messages.length; - if (numMsgs == 0) revert EmptyReport(); - if (numMsgs != report.offchainTokenData.length) revert UnexpectedTokenData(); - - bytes32[] memory hashedLeaves = new bytes32[](numMsgs); - - for (uint256 i = 0; i < numMsgs; ++i) { - Internal.EVM2EVMMessage memory message = report.messages[i]; - // We do this hash here instead of in _verifyMessages to avoid two separate loops - // over the same data, which increases gas cost - hashedLeaves[i] = Internal._hash(message, i_metadataHash); - // For EVM2EVM offramps, the messageID is the leaf hash. - // Asserting that this is true ensures we don't accidentally commit and then execute - // a message with an unexpected hash. - if (hashedLeaves[i] != message.messageId) revert InvalidMessageId(); - } - bool manualExecution = manualExecGasOverrides.length != 0; - - // SECURITY CRITICAL CHECK - uint256 timestampCommitted = ICommitStore(i_commitStore).verify(hashedLeaves, report.proofs, report.proofFlagBits); - if (timestampCommitted == 0) revert RootNotCommitted(); - - // Execute messages - for (uint256 i = 0; i < numMsgs; ++i) { - Internal.EVM2EVMMessage memory message = report.messages[i]; - Internal.MessageExecutionState originalState = getExecutionState(message.sequenceNumber); - // Two valid cases here, we either have never touched this message before, or we tried to execute - // and failed. This check protects against reentry and re-execution because the other state is - // IN_PROGRESS which should not be allowed to execute. - if ( - !( - originalState == Internal.MessageExecutionState.UNTOUCHED - || originalState == Internal.MessageExecutionState.FAILURE - ) - ) { - // If the message has already been executed, we skip it. We want to not revert on race conditions between - // executing parties. This will allow us to open up manual exec while also attempting with the DON, without - // reverting an entire DON batch when a user manually executes while the tx is inflight. - emit SkippedAlreadyExecutedMessage(message.sequenceNumber); - continue; - } - uint32[] memory tokenGasOverrides; - - if (manualExecution) { - tokenGasOverrides = manualExecGasOverrides[i].tokenGasOverrides; - bool isOldCommitReport = - (block.timestamp - timestampCommitted) > s_dynamicConfig.permissionLessExecutionThresholdSeconds; - // Manually execution is fine if we previously failed or if the commit report is just too old - // Acceptable state transitions: FAILURE->SUCCESS, UNTOUCHED->SUCCESS, FAILURE->FAILURE - if (!(isOldCommitReport || originalState == Internal.MessageExecutionState.FAILURE)) { - revert ManualExecutionNotYetEnabled(); - } - - // Manual execution gas limit can override gas limit specified in the message. Value of 0 indicates no override. - if (manualExecGasOverrides[i].receiverExecutionGasLimit != 0) { - message.gasLimit = manualExecGasOverrides[i].receiverExecutionGasLimit; - } - } else { - // DON can only execute a message once - // Acceptable state transitions: UNTOUCHED->SUCCESS, UNTOUCHED->FAILURE - if (originalState != Internal.MessageExecutionState.UNTOUCHED) { - emit AlreadyAttempted(message.sequenceNumber); - continue; - } - } - - if (message.nonce != 0) { - // In the scenario where we upgrade offRamps, we still want to have sequential nonces. - // Referencing the old offRamp to check the expected nonce if none is set for a - // given sender allows us to skip the current message if it would not be the next according - // to the old offRamp. This preserves sequencing between updates. - uint64 prevNonce = s_senderNonce[message.sender]; - if (prevNonce == 0) { - if (i_prevOffRamp != address(0)) { - prevNonce = IAny2EVMOffRamp(i_prevOffRamp).getSenderNonce(message.sender); - if (prevNonce + 1 != message.nonce) { - // the starting v2 onramp nonce, i.e. the 1st message nonce v2 offramp is expected to receive, - // is guaranteed to equal (largest v1 onramp nonce + 1). - // if this message's nonce isn't (v1 offramp nonce + 1), then v1 offramp nonce != largest v1 onramp nonce, - // it tells us there are still messages inflight for v1 offramp - emit SkippedSenderWithPreviousRampMessageInflight(message.nonce, message.sender); - continue; - } - // Otherwise this nonce is indeed the "transitional nonce", that is - // all messages sent to v1 ramp have been executed by the DON and the sequence can resume in V2. - // Note if first time user in V2, then prevNonce will be 0, and message.nonce = 1, so this will be a no-op. - s_senderNonce[message.sender] = prevNonce; - } - } - - // UNTOUCHED messages MUST be executed in order always IF message.nonce > 0. - if (originalState == Internal.MessageExecutionState.UNTOUCHED) { - if (prevNonce + 1 != message.nonce) { - // We skip the message if the nonce is incorrect, since message.nonce > 0. - emit SkippedIncorrectNonce(message.nonce, message.sender); - continue; - } - } - } - - // Although we expect only valid messages will be committed, we check again - // when executing as a defense in depth measure. - bytes[] memory offchainTokenData = report.offchainTokenData[i]; - _isWellFormed( - message.sequenceNumber, - message.sourceChainSelector, - message.tokenAmounts.length, - message.data.length, - offchainTokenData.length - ); - - _setExecutionState(message.sequenceNumber, Internal.MessageExecutionState.IN_PROGRESS); - (Internal.MessageExecutionState newState, bytes memory returnData) = - _trialExecute(message, offchainTokenData, tokenGasOverrides); - _setExecutionState(message.sequenceNumber, newState); - - // Since it's hard to estimate whether manual execution will succeed, we - // revert the entire transaction if it fails. This will show the user if - // their manual exec will fail before they submit it. - if (manualExecution) { - if (newState == Internal.MessageExecutionState.FAILURE) { - if (originalState != Internal.MessageExecutionState.UNTOUCHED) { - // If manual execution fails, we revert the entire transaction, unless the originalState is UNTOUCHED as we - // would still be making progress by changing the state from UNTOUCHED to FAILURE. - revert ExecutionError(returnData); - } - } - } - - // The only valid prior states are UNTOUCHED and FAILURE (checked above) - // The only valid post states are SUCCESS and FAILURE (checked below) - if (newState != Internal.MessageExecutionState.SUCCESS) { - if (newState != Internal.MessageExecutionState.FAILURE) { - revert InvalidNewState(message.sequenceNumber, newState); - } - } - - // Nonce changes per state transition. - // These only apply for ordered messages. - // UNTOUCHED -> FAILURE nonce bump - // UNTOUCHED -> SUCCESS nonce bump - // FAILURE -> FAILURE no nonce bump - // FAILURE -> SUCCESS no nonce bump - if (message.nonce != 0) { - if (originalState == Internal.MessageExecutionState.UNTOUCHED) { - s_senderNonce[message.sender]++; - } - } - - emit ExecutionStateChanged(message.sequenceNumber, message.messageId, newState, returnData); - } - } - - /// @notice Does basic message validation. Should never fail. - /// @param sequenceNumber Sequence number of the message. - /// @param sourceChainSelector SourceChainSelector of the message. - /// @param numberOfTokens Length of tokenAmounts array in the message. - /// @param dataLength Length of data field in the message. - /// @param offchainTokenDataLength Length of offchainTokenData array. - /// @dev reverts on validation failures. - function _isWellFormed( - uint64 sequenceNumber, - uint64 sourceChainSelector, - uint256 numberOfTokens, - uint256 dataLength, - uint256 offchainTokenDataLength - ) private view { - if (sourceChainSelector != i_sourceChainSelector) revert InvalidSourceChain(sourceChainSelector); - if (numberOfTokens > uint256(s_dynamicConfig.maxNumberOfTokensPerMsg)) { - revert UnsupportedNumberOfTokens(sequenceNumber); - } - if (numberOfTokens != offchainTokenDataLength) revert TokenDataMismatch(sequenceNumber); - if (dataLength > uint256(s_dynamicConfig.maxDataBytes)) { - revert MessageTooLarge(uint256(s_dynamicConfig.maxDataBytes), dataLength); - } - } - - /// @notice Try executing a message. - /// @param message Internal.EVM2EVMMessage memory message. - /// @param offchainTokenData Data provided by the DON for token transfers. - /// @return the new state of the message, being either SUCCESS or FAILURE. - /// @return revert data in bytes if CCIP receiver reverted during execution. - function _trialExecute( - Internal.EVM2EVMMessage memory message, - bytes[] memory offchainTokenData, - uint32[] memory tokenGasOverrides - ) internal returns (Internal.MessageExecutionState, bytes memory) { - try this.executeSingleMessage(message, offchainTokenData, tokenGasOverrides) {} - catch (bytes memory err) { - // return the message execution state as FAILURE and the revert data - // Max length of revert data is Router.MAX_RET_BYTES, max length of err is 4 + Router.MAX_RET_BYTES - return (Internal.MessageExecutionState.FAILURE, err); - } - // If message execution succeeded, no CCIP receiver return data is expected, return with empty bytes. - return (Internal.MessageExecutionState.SUCCESS, ""); - } - - /// @notice Execute a single message. - /// @param message The message that will be executed. - /// @param offchainTokenData Token transfer data to be passed to TokenPool. - /// @dev We make this external and callable by the contract itself, in order to try/catch - /// its execution and enforce atomicity among successful message processing and token transfer. - /// @dev We use ERC-165 to check for the ccipReceive interface to permit sending tokens to contracts - /// (for example smart contract wallets) without an associated message. - function executeSingleMessage( - Internal.EVM2EVMMessage calldata message, - bytes[] calldata offchainTokenData, - uint32[] memory tokenGasOverrides - ) external { - if (msg.sender != address(this)) revert CanOnlySelfCall(); - Client.EVMTokenAmount[] memory destTokenAmounts = new Client.EVMTokenAmount[](0); - if (message.tokenAmounts.length > 0) { - destTokenAmounts = _releaseOrMintTokens( - message.tokenAmounts, - abi.encode(message.sender), - message.receiver, - message.sourceTokenData, - offchainTokenData, - tokenGasOverrides - ); - } - // There are three cases in which we skip calling the receiver: - // 1. If the message data is empty AND the gas limit is 0. - // This indicates a message that only transfers tokens. It is valid to only send tokens to a contract - // that supports the IAny2EVMMessageReceiver interface, but without this first check we would call the - // receiver without any gas, which would revert the transaction. - // 2. If the receiver is not a contract. - // 3. If the receiver is a contract but it does not support the IAny2EVMMessageReceiver interface. - // - // The ordering of these checks is important, as the first check is the cheapest to execute. - if ( - (message.data.length == 0 && message.gasLimit == 0) || message.receiver.code.length == 0 - || !message.receiver.supportsInterface(type(IAny2EVMMessageReceiver).interfaceId) - ) return; - - (bool success, bytes memory returnData,) = IRouter(s_dynamicConfig.router).routeMessage( - Client.Any2EVMMessage({ - messageId: message.messageId, - sourceChainSelector: message.sourceChainSelector, - sender: abi.encode(message.sender), - data: message.data, - destTokenAmounts: destTokenAmounts - }), - Internal.GAS_FOR_CALL_EXACT_CHECK, - message.gasLimit, - message.receiver - ); - // If CCIP receiver execution is not successful, revert the call including token transfers - if (!success) revert ReceiverError(returnData); - } - - /// @notice creates a unique hash to be used in message hashing. - function _metadataHash(bytes32 prefix) internal view returns (bytes32) { - return keccak256(abi.encode(prefix, i_sourceChainSelector, i_chainSelector, i_onRamp)); - } - - // ================================================================ - // │ Config │ - // ================================================================ - - /// @notice Returns the static config. - /// @dev This function will always return the same struct as the contents is static and can never change. - /// RMN depends on this function, if changing, please notify the RMN maintainers. - function getStaticConfig() external view returns (StaticConfig memory) { - return StaticConfig({ - commitStore: i_commitStore, - chainSelector: i_chainSelector, - sourceChainSelector: i_sourceChainSelector, - onRamp: i_onRamp, - prevOffRamp: i_prevOffRamp, - rmnProxy: i_rmnProxy, - tokenAdminRegistry: i_tokenAdminRegistry - }); - } - - /// @notice Returns the current dynamic config. - /// @return The current config. - function getDynamicConfig() external view returns (DynamicConfig memory) { - return s_dynamicConfig; - } - - /// @notice Sets the dynamic config. This function is called during `setOCR2Config` flow - function _beforeSetConfig(bytes memory onchainConfig) internal override { - DynamicConfig memory dynamicConfig = abi.decode(onchainConfig, (DynamicConfig)); - - if (dynamicConfig.router == address(0)) revert ZeroAddressNotAllowed(); - - s_dynamicConfig = dynamicConfig; - - emit ConfigSet( - StaticConfig({ - commitStore: i_commitStore, - chainSelector: i_chainSelector, - sourceChainSelector: i_sourceChainSelector, - onRamp: i_onRamp, - prevOffRamp: i_prevOffRamp, - rmnProxy: i_rmnProxy, - tokenAdminRegistry: i_tokenAdminRegistry - }), - dynamicConfig - ); - } - - /// @notice Get all tokens which are included in Aggregate Rate Limiting. - /// @return sourceTokens The source representation of the tokens that are rate limited. - /// @return destTokens The destination representation of the tokens that are rate limited. - /// @dev the order of IDs in the list is **not guaranteed**, therefore, if ordering matters when - /// making successive calls, one should keep the block height constant to ensure a consistent result. - function getAllRateLimitTokens() external view returns (address[] memory sourceTokens, address[] memory destTokens) { - uint256 numRateLimitedTokens = s_rateLimitedTokensDestToSource.length(); - sourceTokens = new address[](numRateLimitedTokens); - destTokens = new address[](numRateLimitedTokens); - - for (uint256 i = 0; i < numRateLimitedTokens; ++i) { - (address destToken, address sourceToken) = s_rateLimitedTokensDestToSource.at(i); - sourceTokens[i] = sourceToken; - destTokens[i] = destToken; - } - return (sourceTokens, destTokens); - } - - /// @notice Adds or removes tokens from being used in Aggregate Rate Limiting. - /// @param removes - A list of one or more tokens to be removed. - /// @param adds - A list of one or more tokens to be added. - function updateRateLimitTokens(RateLimitToken[] memory removes, RateLimitToken[] memory adds) external onlyOwner { - for (uint256 i = 0; i < removes.length; ++i) { - if (s_rateLimitedTokensDestToSource.remove(removes[i].destToken)) { - emit TokenAggregateRateLimitRemoved(removes[i].sourceToken, removes[i].destToken); - } - } - - for (uint256 i = 0; i < adds.length; ++i) { - if (s_rateLimitedTokensDestToSource.set(adds[i].destToken, adds[i].sourceToken)) { - emit TokenAggregateRateLimitAdded(adds[i].sourceToken, adds[i].destToken); - } - } - } - - // ================================================================ - // │ Tokens and pools │ - // ================================================================ - - /// @notice Uses a pool to release or mint a token to a receiver address, with balance checks before and after the - /// transfer. This is done to ensure the exact number of tokens the pool claims to release are actually transferred. - /// @dev The local token address is validated through the TokenAdminRegistry. If, due to some misconfiguration, the - /// token is unknown to the registry, the offRamp will revert. The tx, and the tokens, can be retrieved by - /// registering the token on this chain, and re-trying the msg. - /// @param sourceAmount The amount of tokens to be released/minted. - /// @param originalSender The message sender on the source chain. - /// @param receiver The address that will receive the tokens. - /// @param sourceTokenData A struct containing the local token address, the source pool address and optional data - /// returned from the source pool. - /// @param offchainTokenData Data fetched offchain by the DON. - function _releaseOrMintToken( - uint256 sourceAmount, - bytes memory originalSender, - address receiver, - Internal.SourceTokenData memory sourceTokenData, - bytes memory offchainTokenData - ) internal returns (Client.EVMTokenAmount memory destTokenAmount) { - // We need to safely decode the token address from the sourceTokenData, as it could be wrong, - // in which case it doesn't have to be a valid EVM address. - address localToken = Internal._validateEVMAddress(sourceTokenData.destTokenAddress); - // We check with the token admin registry if the token has a pool on this chain. - address localPoolAddress = ITokenAdminRegistry(i_tokenAdminRegistry).getPool(localToken); - // This will call the supportsInterface through the ERC165Checker, and not directly on the pool address. - // This is done to prevent a pool from reverting the entire transaction if it doesn't support the interface. - // The call gets a max or 30k gas per instance, of which there are three. This means gas estimations should - // account for 90k gas overhead due to the interface check. - if (localPoolAddress == address(0) || !localPoolAddress.supportsInterface(Pool.CCIP_POOL_V1)) { - revert NotACompatiblePool(localPoolAddress); - } - - // We retrieve the local token balance of the receiver before the pool call. - (uint256 balancePre, uint256 gasLeft) = _getBalanceOfReceiver(receiver, localToken, sourceTokenData.destGasAmount); - - // We determined that the pool address is a valid EVM address, but that does not mean the code at this - // address is a (compatible) pool contract. _callWithExactGasSafeReturnData will check if the location - // contains a contract. If it doesn't it reverts with a known error, which we catch gracefully. - // We call the pool with exact gas to increase resistance against malicious tokens or token pools. - // We protects against return data bombs by capping the return data size at MAX_RET_BYTES. - (bool success, bytes memory returnData, uint256 gasUsedReleaseOrMint) = CallWithExactGas - ._callWithExactGasSafeReturnData( - abi.encodeCall( - IPoolV1.releaseOrMint, - Pool.ReleaseOrMintInV1({ - originalSender: originalSender, - receiver: receiver, - amount: sourceAmount, - localToken: localToken, - remoteChainSelector: i_sourceChainSelector, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: sourceTokenData.extraData, - offchainTokenData: offchainTokenData - }) - ), - localPoolAddress, - gasLeft, - Internal.GAS_FOR_CALL_EXACT_CHECK, - Internal.MAX_RET_BYTES - ); - - // wrap and rethrow the error so we can catch it lower in the stack - if (!success) revert TokenHandlingError(returnData); - // If the call was successful, the returnData should contain only the local token amount. - if (returnData.length != Pool.CCIP_POOL_V1_RET_BYTES) { - revert InvalidDataLength(Pool.CCIP_POOL_V1_RET_BYTES, returnData.length); - } - - uint256 localAmount = abi.decode(returnData, (uint256)); - // We don't need to do balance checks if the pool is the receiver, as they would always fail in the case - // of a lockRelease pool. - if (receiver != localPoolAddress) { - (uint256 balancePost,) = _getBalanceOfReceiver(receiver, localToken, gasLeft - gasUsedReleaseOrMint); - - // First we check if the subtraction would result in an underflow to ensure we revert with a clear error - if (balancePost < balancePre || balancePost - balancePre != localAmount) { - revert ReleaseOrMintBalanceMismatch(localAmount, balancePre, balancePost); - } - } - - return Client.EVMTokenAmount({token: localToken, amount: localAmount}); - } - - function _getBalanceOfReceiver( - address receiver, - address token, - uint256 gasLimit - ) internal returns (uint256 balance, uint256 gasLeft) { - (bool success, bytes memory returnData, uint256 gasUsed) = CallWithExactGas._callWithExactGasSafeReturnData( - abi.encodeCall(IERC20.balanceOf, (receiver)), - token, - gasLimit, - Internal.GAS_FOR_CALL_EXACT_CHECK, - Internal.MAX_RET_BYTES - ); - if (!success) revert TokenHandlingError(returnData); - - // If the call was successful, the returnData should contain only the balance. - if (returnData.length != Internal.MAX_BALANCE_OF_RET_BYTES) { - revert InvalidDataLength(Internal.MAX_BALANCE_OF_RET_BYTES, returnData.length); - } - - // Return the decoded balance, which cannot fail as we checked the length, and the gas that is left - // after this call. - return (abi.decode(returnData, (uint256)), gasLimit - gasUsed); - } - - /// @notice Uses pools to release or mint a number of different tokens to a receiver address. - /// @param sourceTokenAmounts List of tokens and amount values to be released/minted. - /// @param originalSender The message sender. - /// @param receiver The address that will receive the tokens. - /// @param encodedSourceTokenData Array of token data returned by token pools on the source chain. - /// @param offchainTokenData Array of token data fetched offchain by the DON. - /// @dev This function wrappes the token pool call in a try catch block to gracefully handle - /// any non-rate limiting errors that may occur. If we encounter a rate limiting related error - /// we bubble it up. If we encounter a non-rate limiting error we wrap it in a TokenHandlingError. - function _releaseOrMintTokens( - Client.EVMTokenAmount[] calldata sourceTokenAmounts, - bytes memory originalSender, - address receiver, - bytes[] calldata encodedSourceTokenData, - bytes[] calldata offchainTokenData, - uint32[] memory tokenGasOverrides - ) internal returns (Client.EVMTokenAmount[] memory destTokenAmounts) { - // Creating a copy is more gas efficient than initializing a new array. - destTokenAmounts = sourceTokenAmounts; - uint256 value = 0; - for (uint256 i = 0; i < sourceTokenAmounts.length; ++i) { - Internal.SourceTokenData memory sourceTokenData = - abi.decode(encodedSourceTokenData[i], (Internal.SourceTokenData)); - if (tokenGasOverrides.length != 0) { - if (tokenGasOverrides[i] != 0) { - sourceTokenData.destGasAmount = tokenGasOverrides[i]; - } - } - destTokenAmounts[i] = _releaseOrMintToken( - sourceTokenAmounts[i].amount, - originalSender, - receiver, - // This should never revert as the onRamp encodes the sourceTokenData struct. Only the inner components from - // this struct come from untrusted sources. - sourceTokenData, - offchainTokenData[i] - ); - - if (s_rateLimitedTokensDestToSource.contains(destTokenAmounts[i].token)) { - value += _getTokenValue(destTokenAmounts[i], IPriceRegistry(s_dynamicConfig.priceRegistry)); - } - } - - if (value > 0) _rateLimitValue(value); - - return destTokenAmounts; - } - - // ================================================================ - // │ Access │ - // ================================================================ - - /// @notice Reverts as this contract should not access CCIP messages - function ccipReceive(Client.Any2EVMMessage calldata) external pure { - // solhint-disable-next-line - revert(); - } -} diff --git a/contracts/src/v0.8/ccip/offRamp/OffRamp.sol b/contracts/src/v0.8/ccip/offRamp/OffRamp.sol index 14d5ae5c75c..8c1585f29a0 100644 --- a/contracts/src/v0.8/ccip/offRamp/OffRamp.sol +++ b/contracts/src/v0.8/ccip/offRamp/OffRamp.sol @@ -269,7 +269,7 @@ contract OffRamp is ITypeAndVersion, MultiOCR3Base { /// insufficient gas provided. /// The reports do not have to contain all the messages (they can be omitted). Multiple reports can be passed in simultaneously. function manuallyExecute( - Internal.ExecutionReportSingleChain[] memory reports, + Internal.ExecutionReport[] memory reports, GasLimitOverride[][] memory gasLimitOverrides ) external { // We do this here because the other _execute path is already covered by MultiOCR3Base. @@ -279,7 +279,7 @@ contract OffRamp is ITypeAndVersion, MultiOCR3Base { if (numReports != gasLimitOverrides.length) revert ManualExecutionGasLimitMismatch(); for (uint256 reportIndex = 0; reportIndex < numReports; ++reportIndex) { - Internal.ExecutionReportSingleChain memory report = reports[reportIndex]; + Internal.ExecutionReport memory report = reports[reportIndex]; uint256 numMsgs = report.messages.length; GasLimitOverride[] memory msgGasLimitOverrides = gasLimitOverrides[reportIndex]; @@ -323,7 +323,7 @@ contract OffRamp is ITypeAndVersion, MultiOCR3Base { /// and expects the exec plugin type to be configured with no signatures. /// @param report serialized execution report function execute(bytes32[3] calldata reportContext, bytes calldata report) external { - _batchExecute(abi.decode(report, (Internal.ExecutionReportSingleChain[])), new GasLimitOverride[][](0)); + _batchExecute(abi.decode(report, (Internal.ExecutionReport[])), new GasLimitOverride[][](0)); bytes32[] memory emptySigs = new bytes32[](0); _transmit(uint8(Internal.OCRPluginType.Execution), reportContext, report, emptySigs, emptySigs, bytes32("")); @@ -337,7 +337,7 @@ contract OffRamp is ITypeAndVersion, MultiOCR3Base { /// @dev The manualExecGasLimits array should either be empty, or match the length of the reports array /// @dev If called from manual execution, each inner array's length has to match the number of messages. function _batchExecute( - Internal.ExecutionReportSingleChain[] memory reports, + Internal.ExecutionReport[] memory reports, GasLimitOverride[][] memory manualExecGasOverrides ) internal { if (reports.length == 0) revert EmptyReport(); @@ -357,7 +357,7 @@ contract OffRamp is ITypeAndVersion, MultiOCR3Base { /// @dev If called from the DON, this array is always empty. /// @dev If called from manual execution, this array is always same length as messages. function _executeSingleReport( - Internal.ExecutionReportSingleChain memory report, + Internal.ExecutionReport memory report, GasLimitOverride[] memory manualExecGasExecOverrides ) internal { uint64 sourceChainSelector = report.sourceChainSelector; diff --git a/contracts/src/v0.8/ccip/onRamp/EVM2EVMOnRamp.sol b/contracts/src/v0.8/ccip/onRamp/EVM2EVMOnRamp.sol deleted file mode 100644 index 0c48b10e64d..00000000000 --- a/contracts/src/v0.8/ccip/onRamp/EVM2EVMOnRamp.sol +++ /dev/null @@ -1,921 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; -import {IEVM2AnyOnRamp} from "../interfaces/IEVM2AnyOnRamp.sol"; -import {IEVM2AnyOnRampClient} from "../interfaces/IEVM2AnyOnRampClient.sol"; -import {IPoolV1} from "../interfaces/IPool.sol"; -import {IPriceRegistry} from "../interfaces/IPriceRegistry.sol"; -import {IRMN} from "../interfaces/IRMN.sol"; -import {ITokenAdminRegistry} from "../interfaces/ITokenAdminRegistry.sol"; -import {ILinkAvailable} from "../interfaces/automation/ILinkAvailable.sol"; - -import {AggregateRateLimiter} from "../AggregateRateLimiter.sol"; -import {Client} from "../libraries/Client.sol"; -import {Internal} from "../libraries/Internal.sol"; -import {Pool} from "../libraries/Pool.sol"; -import {RateLimiter} from "../libraries/RateLimiter.sol"; -import {USDPriceWith18Decimals} from "../libraries/USDPriceWith18Decimals.sol"; - -import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; -import {EnumerableMap} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/structs/EnumerableMap.sol"; - -/// @notice The onRamp is a contract that handles lane-specific fee logic, NOP payments and -/// bridgeable token support. -/// @dev The EVM2EVMOnRamp, CommitStore and EVM2EVMOffRamp form an xchain upgradeable unit. Any change to one of them -/// results an onchain upgrade of all 3. -contract EVM2EVMOnRamp is IEVM2AnyOnRamp, ILinkAvailable, AggregateRateLimiter, ITypeAndVersion { - using SafeERC20 for IERC20; - using EnumerableMap for EnumerableMap.AddressToUintMap; - using USDPriceWith18Decimals for uint224; - - error InvalidExtraArgsTag(); - error ExtraArgOutOfOrderExecutionMustBeTrue(); - error OnlyCallableByOwnerOrAdmin(); - error OnlyCallableByOwnerOrAdminOrNop(); - error InvalidWithdrawParams(); - error NoFeesToPay(); - error NoNopsToPay(); - error InsufficientBalance(); - error TooManyNops(); - error MaxFeeBalanceReached(); - error MessageTooLarge(uint256 maxSize, uint256 actualSize); - error MessageGasLimitTooHigh(); - error UnsupportedNumberOfTokens(); - error UnsupportedToken(address token); - error MustBeCalledByRouter(); - error RouterMustSetOriginalSender(); - error InvalidConfig(); - error CursedByRMN(); - error LinkBalanceNotSettled(); - error InvalidNopAddress(address nop); - error NotAFeeToken(address token); - error CannotSendZeroTokens(); - error SourceTokenDataTooLarge(address token); - error InvalidChainSelector(uint64 chainSelector); - error GetSupportedTokensFunctionalityRemovedCheckAdminRegistry(); - error InvalidDestBytesOverhead(address token, uint32 destBytesOverhead); - - event ConfigSet(StaticConfig staticConfig, DynamicConfig dynamicConfig); - event NopPaid(address indexed nop, uint256 amount); - event FeeConfigSet(FeeTokenConfigArgs[] feeConfig); - event TokenTransferFeeConfigSet(TokenTransferFeeConfigArgs[] transferFeeConfig); - event TokenTransferFeeConfigDeleted(address[] tokens); - /// RMN depends on this event, if changing, please notify the RMN maintainers. - event CCIPSendRequested(Internal.EVM2EVMMessage message); - event NopsSet(uint256 nopWeightsTotal, NopAndWeight[] nopsAndWeights); - - /// @dev Struct that contains the static configuration - /// RMN depends on this struct, if changing, please notify the RMN maintainers. - //solhint-disable gas-struct-packing - struct StaticConfig { - address linkToken; // ────────╮ Link token address - uint64 chainSelector; // ─────╯ Source chainSelector - uint64 destChainSelector; // ─╮ Destination chainSelector - uint64 defaultTxGasLimit; // │ Default gas limit for a tx - uint96 maxNopFeesJuels; // ───╯ Max nop fee balance onramp can have - address prevOnRamp; // Address of previous-version OnRamp - address rmnProxy; // Address of RMN proxy - address tokenAdminRegistry; // Address of the token admin registry - } - - /// @dev Struct to contains the dynamic configuration - struct DynamicConfig { - address router; // ──────────────────────────╮ Router address - uint16 maxNumberOfTokensPerMsg; // │ Maximum number of distinct ERC20 token transferred per message - uint32 destGasOverhead; // │ Gas charged on top of the gasLimit to cover destination chain costs - uint16 destGasPerPayloadByte; // │ Destination chain gas charged for passing each byte of `data` payload to receiver - uint32 destDataAvailabilityOverheadGas; // ──╯ Extra data availability gas charged on top of the message, e.g. for OCR - uint16 destGasPerDataAvailabilityByte; // ───╮ Amount of gas to charge per byte of message data that needs availability - uint16 destDataAvailabilityMultiplierBps; // │ Multiplier for data availability gas, multiples of bps, or 0.0001 - address priceRegistry; // │ Price registry address - uint32 maxDataBytes; // │ Maximum payload data size in bytes - uint32 maxPerMsgGasLimit; // ────────────────╯ Maximum gas limit for messages targeting EVMs - // │ - // The following three properties are defaults, they can be overridden by setting the TokenTransferFeeConfig for a token - uint16 defaultTokenFeeUSDCents; // ──────────╮ Default token fee charged per token transfer - uint32 defaultTokenDestGasOverhead; // │ Default gas charged to execute the token transfer on the destination chain - bool enforceOutOfOrder; // ──────────────────╯ Whether to enforce the allowOutOfOrderExecution extraArg value to be true. - } - - /// @dev Struct to hold the execution fee configuration for a fee token - struct FeeTokenConfig { - uint32 networkFeeUSDCents; // ─────────╮ Flat network fee to charge for messages, multiples of 0.01 USD - uint64 gasMultiplierWeiPerEth; // │ Multiplier for gas costs, 1e18 based so 11e17 = 10% extra cost. - uint64 premiumMultiplierWeiPerEth; // │ Multiplier for fee-token-specific premiums - bool enabled; // ──────────────────────╯ Whether this fee token is enabled - } - - /// @dev Struct to hold the fee configuration for a fee token, same as the FeeTokenConfig but with - /// token included so that an array of these can be passed in to setFeeTokenConfig to set the mapping - struct FeeTokenConfigArgs { - address token; // ─────────────────────╮ Token address - uint32 networkFeeUSDCents; // │ Flat network fee to charge for messages, multiples of 0.01 USD - uint64 gasMultiplierWeiPerEth; // ─────╯ Multiplier for gas costs, 1e18 based so 11e17 = 10% extra cost - uint64 premiumMultiplierWeiPerEth; // ─╮ Multiplier for fee-token-specific premiums, 1e18 based - bool enabled; // ──────────────────────╯ Whether this fee token is enabled - } - - /// @dev Struct to hold the transfer fee configuration for token transfers - struct TokenTransferFeeConfig { - uint32 minFeeUSDCents; // ──────────╮ Minimum fee to charge per token transfer, multiples of 0.01 USD - uint32 maxFeeUSDCents; // │ Maximum fee to charge per token transfer, multiples of 0.01 USD - uint16 deciBps; // │ Basis points charged on token transfers, multiples of 0.1bps, or 1e-5 - uint32 destGasOverhead; // │ Gas charged to execute the token transfer on the destination chain - // │ Extra data availability bytes that are returned from the source pool and sent - uint32 destBytesOverhead; // │ to the destination pool. Must be >= Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES - bool aggregateRateLimitEnabled; // │ Whether this transfer token is to be included in Aggregate Rate Limiting - bool isEnabled; // ─────────────────╯ Whether this token has custom transfer fees - } - - /// @dev Same as TokenTransferFeeConfig - /// token included so that an array of these can be passed in to setTokenTransferFeeConfig - struct TokenTransferFeeConfigArgs { - address token; // ──────────────────╮ Token address - uint32 minFeeUSDCents; // │ Minimum fee to charge per token transfer, multiples of 0.01 USD - uint32 maxFeeUSDCents; // │ Maximum fee to charge per token transfer, multiples of 0.01 USD - uint16 deciBps; // ─────────────────╯ Basis points charged on token transfers, multiples of 0.1bps, or 1e-5 - uint32 destGasOverhead; // ─────────╮ Gas charged to execute the token transfer on the destination chain - // │ Extra data availability bytes that are returned from the source pool and sent - uint32 destBytesOverhead; // │ to the destination pool. Must be >= Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES - bool aggregateRateLimitEnabled; // ─╯ Whether this transfer token is to be included in Aggregate Rate Limiting - } - - /// @dev Nop address and weight, used to set the nops and their weights - struct NopAndWeight { - address nop; // ────╮ Address of the node operator - uint16 weight; // ──╯ Weight for nop rewards - } - - // STATIC CONFIG - string public constant override typeAndVersion = "EVM2EVMOnRamp 1.5.0"; - /// @dev metadataHash is a lane-specific prefix for a message hash preimage which ensures global uniqueness - /// Ensures that 2 identical messages sent to 2 different lanes will have a distinct hash. - /// Must match the metadataHash used in computing leaf hashes offchain for the root committed in - /// the commitStore and i_metadataHash in the offRamp. - bytes32 internal immutable i_metadataHash; - /// @dev Default gas limit for a transactions that did not specify - /// a gas limit in the extraArgs. - uint64 internal immutable i_defaultTxGasLimit; - /// @dev Maximum nop fee that can accumulate in this onramp - uint96 internal immutable i_maxNopFeesJuels; - /// @dev The link token address - known to pay nops for their work - address internal immutable i_linkToken; - /// @dev The chain ID of the source chain that this contract is deployed to - uint64 internal immutable i_chainSelector; - /// @dev The chain ID of the destination chain - uint64 internal immutable i_destChainSelector; - /// @dev The address of previous-version OnRamp for this lane - /// Used to be able to provide sequencing continuity during a zero downtime upgrade. - address internal immutable i_prevOnRamp; - /// @dev The address of the RMN proxy - address internal immutable i_rmnProxy; - /// @dev The address of the token admin registry - address internal immutable i_tokenAdminRegistry; - /// @dev the maximum number of nops that can be configured at the same time. - /// Used to bound gas for loops over nops. - uint256 private constant MAX_NUMBER_OF_NOPS = 64; - - // DYNAMIC CONFIG - /// @dev The config for the onRamp - DynamicConfig internal s_dynamicConfig; - /// @dev (address nop => uint256 weight) - EnumerableMap.AddressToUintMap internal s_nops; - - /// @dev The execution fee token config that can be set by the owner or fee admin - mapping(address token => FeeTokenConfig feeTokenConfig) internal s_feeTokenConfig; - /// @dev The token transfer fee config that can be set by the owner or fee admin - mapping(address token => TokenTransferFeeConfig tranferFeeConfig) internal s_tokenTransferFeeConfig; - - // STATE - /// @dev The current nonce per sender. - /// The offramp has a corresponding s_senderNonce mapping to ensure messages - /// are executed in the same order they are sent. - mapping(address sender => uint64 nonce) internal s_senderNonce; - /// @dev The amount of LINK available to pay NOPS - uint96 internal s_nopFeesJuels; - /// @dev The combined weight of all NOPs weights - uint32 internal s_nopWeightsTotal; - /// @dev The last used sequence number. This is zero in the case where no - /// messages has been sent yet. 0 is not a valid sequence number for any - /// real transaction. - uint64 internal s_sequenceNumber; - - constructor( - StaticConfig memory staticConfig, - DynamicConfig memory dynamicConfig, - RateLimiter.Config memory rateLimiterConfig, - FeeTokenConfigArgs[] memory feeTokenConfigs, - TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs, - NopAndWeight[] memory nopsAndWeights - ) AggregateRateLimiter(rateLimiterConfig) { - if ( - staticConfig.linkToken == address(0) || staticConfig.chainSelector == 0 || staticConfig.destChainSelector == 0 - || staticConfig.defaultTxGasLimit == 0 || staticConfig.rmnProxy == address(0) - || staticConfig.tokenAdminRegistry == address(0) - ) revert InvalidConfig(); - - i_metadataHash = keccak256( - abi.encode( - Internal.EVM_2_EVM_MESSAGE_HASH, staticConfig.chainSelector, staticConfig.destChainSelector, address(this) - ) - ); - i_linkToken = staticConfig.linkToken; - i_chainSelector = staticConfig.chainSelector; - i_destChainSelector = staticConfig.destChainSelector; - i_defaultTxGasLimit = staticConfig.defaultTxGasLimit; - i_maxNopFeesJuels = staticConfig.maxNopFeesJuels; - i_prevOnRamp = staticConfig.prevOnRamp; - i_rmnProxy = staticConfig.rmnProxy; - i_tokenAdminRegistry = staticConfig.tokenAdminRegistry; - - _setDynamicConfig(dynamicConfig); - _setFeeTokenConfig(feeTokenConfigs); - _setTokenTransferFeeConfig(tokenTransferFeeConfigArgs, new address[](0)); - _setNops(nopsAndWeights); - } - - // ================================================================ - // │ Messaging │ - // ================================================================ - - /// @inheritdoc IEVM2AnyOnRamp - function getExpectedNextSequenceNumber() external view returns (uint64) { - return s_sequenceNumber + 1; - } - - /// @inheritdoc IEVM2AnyOnRamp - function getSenderNonce(address sender) external view returns (uint64) { - uint256 senderNonce = s_senderNonce[sender]; - - if (i_prevOnRamp != address(0)) { - if (senderNonce == 0) { - // If OnRamp was upgraded, check if sender has a nonce from the previous OnRamp. - return IEVM2AnyOnRamp(i_prevOnRamp).getSenderNonce(sender); - } - } - return uint64(senderNonce); - } - - /// @inheritdoc IEVM2AnyOnRampClient - function forwardFromRouter( - uint64 destChainSelector, - Client.EVM2AnyMessage calldata message, - uint256 feeTokenAmount, - address originalSender - ) external returns (bytes32) { - if (IRMN(i_rmnProxy).isCursed(bytes16(uint128(destChainSelector)))) revert CursedByRMN(); - // Validate message sender is set and allowed. Not validated in `getFee` since it is not user-driven. - if (originalSender == address(0)) revert RouterMustSetOriginalSender(); - // Router address may be zero intentionally to pause. - if (msg.sender != s_dynamicConfig.router) revert MustBeCalledByRouter(); - if (destChainSelector != i_destChainSelector) revert InvalidChainSelector(destChainSelector); - - Client.EVMExtraArgsV2 memory extraArgs = _fromBytes(message.extraArgs); - // Validate the message with various checks - uint256 numberOfTokens = message.tokenAmounts.length; - _validateMessage(message.data.length, extraArgs.gasLimit, numberOfTokens, extraArgs.allowOutOfOrderExecution); - - // Only check token value if there are tokens - if (numberOfTokens > 0) { - uint256 value; - for (uint256 i = 0; i < numberOfTokens; ++i) { - if (message.tokenAmounts[i].amount == 0) revert CannotSendZeroTokens(); - if (s_tokenTransferFeeConfig[message.tokenAmounts[i].token].aggregateRateLimitEnabled) { - value += _getTokenValue(message.tokenAmounts[i], IPriceRegistry(s_dynamicConfig.priceRegistry)); - } - } - // Rate limit on aggregated token value - if (value > 0) _rateLimitValue(value); - } - - // Convert feeToken to link if not already in link - if (message.feeToken == i_linkToken) { - // Since there is only 1b link this is safe - s_nopFeesJuels += uint96(feeTokenAmount); - } else { - // the cast from uint256 to uint96 is considered safe, uint96 can store more than max supply of link token - s_nopFeesJuels += uint96( - IPriceRegistry(s_dynamicConfig.priceRegistry).convertTokenAmount(message.feeToken, feeTokenAmount, i_linkToken) - ); - } - if (s_nopFeesJuels > i_maxNopFeesJuels) revert MaxFeeBalanceReached(); - - // Get the current nonce if the message is an ordered message. If it's not ordered, we don't have to make the - // external call. - if (!extraArgs.allowOutOfOrderExecution) { - if (i_prevOnRamp != address(0)) { - if (s_senderNonce[originalSender] == 0) { - // If this is first time send for a sender in new OnRamp, check if they have a nonce - // from the previous OnRamp and start from there instead of zero. - s_senderNonce[originalSender] = IEVM2AnyOnRamp(i_prevOnRamp).getSenderNonce(originalSender); - } - } - } - - // We need the next available sequence number so we increment before we use the value - Internal.EVM2EVMMessage memory newMessage = Internal.EVM2EVMMessage({ - sourceChainSelector: i_chainSelector, - sender: originalSender, - // EVM destination addresses should be abi encoded and therefore always 32 bytes long - // Not duplicately validated in `getFee`. Invalid address is uncommon, gas cost outweighs UX gain. - receiver: Internal._validateEVMAddress(message.receiver), - sequenceNumber: ++s_sequenceNumber, - gasLimit: extraArgs.gasLimit, - strict: false, - // Only bump nonce for messages that specify allowOutOfOrderExecution == false. Otherwise, we - // may block ordered message nonces, which is not what we want. - nonce: extraArgs.allowOutOfOrderExecution ? 0 : ++s_senderNonce[originalSender], - feeToken: message.feeToken, - feeTokenAmount: feeTokenAmount, - data: message.data, - tokenAmounts: message.tokenAmounts, - sourceTokenData: new bytes[](numberOfTokens), // will be populated below - messageId: "" - }); - - // Lock the tokens as last step. TokenPools may not always be trusted. - // There should be no state changes after external call to TokenPools. - for (uint256 i = 0; i < numberOfTokens; ++i) { - Client.EVMTokenAmount memory tokenAndAmount = message.tokenAmounts[i]; - IPoolV1 sourcePool = getPoolBySourceToken(destChainSelector, IERC20(tokenAndAmount.token)); - // We don't have to check if it supports the pool version in a non-reverting way here because - // if we revert here, there is no effect on CCIP. Therefore we directly call the supportsInterface - // function and not through the ERC165Checker. - if (address(sourcePool) == address(0) || !sourcePool.supportsInterface(Pool.CCIP_POOL_V1)) { - revert UnsupportedToken(tokenAndAmount.token); - } - - Pool.LockOrBurnOutV1 memory poolReturnData = sourcePool.lockOrBurn( - Pool.LockOrBurnInV1({ - receiver: message.receiver, - remoteChainSelector: i_destChainSelector, - originalSender: originalSender, - amount: tokenAndAmount.amount, - localToken: tokenAndAmount.token - }) - ); - - // Since the DON has to pay for the extraData to be included on the destination chain, we cap the length of the - // extraData. This prevents gas bomb attacks on the NOPs. As destBytesOverhead accounts for both - // extraData and offchainData, this caps the worst case abuse to the number of bytes reserved for offchainData. - if (poolReturnData.destPoolData.length > Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) { - // If TokenTransferFeeConfig.enabled is false, there is no config. That means destBytesOverhead is zero and - // this check is always true. That ensures that a pool without config cannot send more than - // Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES bytes of data. - if (poolReturnData.destPoolData.length > s_tokenTransferFeeConfig[tokenAndAmount.token].destBytesOverhead) { - revert SourceTokenDataTooLarge(tokenAndAmount.token); - } - } - - // We validate the token address to ensure it is a valid EVM address - Internal._validateEVMAddress(poolReturnData.destTokenAddress); - - newMessage.sourceTokenData[i] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(sourcePool), - destTokenAddress: poolReturnData.destTokenAddress, - extraData: poolReturnData.destPoolData, - // The user will be billed either the default or the override, so we send the exact amount that we billed for - // to the destination chain to be used for the token releaseOrMint and transfer. - destGasAmount: s_tokenTransferFeeConfig[tokenAndAmount.token].isEnabled - ? s_tokenTransferFeeConfig[tokenAndAmount.token].destGasOverhead - : s_dynamicConfig.defaultTokenDestGasOverhead - }) - ); - } - - // Hash only after the sourceTokenData has been set - newMessage.messageId = Internal._hash(newMessage, i_metadataHash); - - // Emit message request - // This must happen after any pool events as some tokens (e.g. USDC) emit events that we expect to precede this - // event in the offchain code. - emit CCIPSendRequested(newMessage); - return newMessage.messageId; - } - - /// @dev Convert the extra args bytes into a struct - /// @param extraArgs The extra args bytes - /// @return The extra args struct - function _fromBytes(bytes calldata extraArgs) internal view returns (Client.EVMExtraArgsV2 memory) { - if (extraArgs.length == 0) { - return Client.EVMExtraArgsV2({gasLimit: i_defaultTxGasLimit, allowOutOfOrderExecution: false}); - } - - bytes4 extraArgsTag = bytes4(extraArgs); - if (extraArgsTag == Client.EVM_EXTRA_ARGS_V2_TAG) { - return abi.decode(extraArgs[4:], (Client.EVMExtraArgsV2)); - } else if (extraArgsTag == Client.EVM_EXTRA_ARGS_V1_TAG) { - // EVMExtraArgsV1 originally included a second boolean (strict) field which has been deprecated. - // Clients may still include it but it will be ignored. - return Client.EVMExtraArgsV2({gasLimit: abi.decode(extraArgs[4:], (uint256)), allowOutOfOrderExecution: false}); - } - - revert InvalidExtraArgsTag(); - } - - /// @notice Validate the forwarded message with various checks. - /// @dev This function can be called multiple times during a CCIPSend, - /// only common user-driven mistakes are validated here to minimize duplicate validation cost. - /// @param dataLength The length of the data field of the message. - /// @param gasLimit The gasLimit set in message for destination execution. - /// @param numberOfTokens The number of tokens to be sent. - function _validateMessage( - uint256 dataLength, - uint256 gasLimit, - uint256 numberOfTokens, - bool allowOutOfOrderExecution - ) internal view { - uint256 maxDataBytes = uint256(s_dynamicConfig.maxDataBytes); - if (dataLength > maxDataBytes) revert MessageTooLarge(maxDataBytes, dataLength); - if (gasLimit > uint256(s_dynamicConfig.maxPerMsgGasLimit)) revert MessageGasLimitTooHigh(); - if (numberOfTokens > uint256(s_dynamicConfig.maxNumberOfTokensPerMsg)) revert UnsupportedNumberOfTokens(); - if (!allowOutOfOrderExecution) { - if (s_dynamicConfig.enforceOutOfOrder) { - revert ExtraArgOutOfOrderExecutionMustBeTrue(); - } - } - } - - // ================================================================ - // │ Config │ - // ================================================================ - - /// @notice Returns the static onRamp config. - /// @dev RMN depends on this function, if changing, please notify the RMN maintainers. - /// @return the configuration. - function getStaticConfig() external view returns (StaticConfig memory) { - return StaticConfig({ - linkToken: i_linkToken, - chainSelector: i_chainSelector, - destChainSelector: i_destChainSelector, - defaultTxGasLimit: i_defaultTxGasLimit, - maxNopFeesJuels: i_maxNopFeesJuels, - prevOnRamp: i_prevOnRamp, - rmnProxy: i_rmnProxy, - tokenAdminRegistry: i_tokenAdminRegistry - }); - } - - /// @notice Returns the dynamic onRamp config. - /// @return dynamicConfig the configuration. - function getDynamicConfig() external view returns (DynamicConfig memory dynamicConfig) { - return s_dynamicConfig; - } - - /// @notice Sets the dynamic configuration. - /// @param dynamicConfig The configuration. - function setDynamicConfig(DynamicConfig memory dynamicConfig) external onlyOwner { - _setDynamicConfig(dynamicConfig); - } - - /// @notice Internal version of setDynamicConfig to allow for reuse in the constructor. - function _setDynamicConfig(DynamicConfig memory dynamicConfig) internal { - // We permit router to be set to zero as a way to pause the contract. - if (dynamicConfig.priceRegistry == address(0)) revert InvalidConfig(); - s_dynamicConfig = dynamicConfig; - - emit ConfigSet( - StaticConfig({ - linkToken: i_linkToken, - chainSelector: i_chainSelector, - destChainSelector: i_destChainSelector, - defaultTxGasLimit: i_defaultTxGasLimit, - maxNopFeesJuels: i_maxNopFeesJuels, - prevOnRamp: i_prevOnRamp, - rmnProxy: i_rmnProxy, - tokenAdminRegistry: i_tokenAdminRegistry - }), - dynamicConfig - ); - } - - // ================================================================ - // │ Tokens and pools │ - // ================================================================ - - /// @inheritdoc IEVM2AnyOnRampClient - function getPoolBySourceToken(uint64, /*destChainSelector*/ IERC20 sourceToken) public view returns (IPoolV1) { - return IPoolV1(ITokenAdminRegistry(i_tokenAdminRegistry).getPool(address(sourceToken))); - } - - /// @inheritdoc IEVM2AnyOnRampClient - function getSupportedTokens(uint64) external pure returns (address[] memory) { - revert GetSupportedTokensFunctionalityRemovedCheckAdminRegistry(); - } - - // ================================================================ - // │ Fees │ - // ================================================================ - - /// @inheritdoc IEVM2AnyOnRampClient - /// @dev getFee MUST revert if the feeToken is not listed in the fee token config, as the router assumes it does. - /// @param destChainSelector The destination chain selector. - /// @param message The message to get quote for. - /// @return feeTokenAmount The amount of fee token needed for the fee, in smallest denomination of the fee token. - function getFee( - uint64 destChainSelector, - Client.EVM2AnyMessage calldata message - ) external view returns (uint256 feeTokenAmount) { - if (destChainSelector != i_destChainSelector) revert InvalidChainSelector(destChainSelector); - - Client.EVMExtraArgsV2 memory extraArgs = _fromBytes(message.extraArgs); - // Validate the message with various checks - _validateMessage( - message.data.length, extraArgs.gasLimit, message.tokenAmounts.length, extraArgs.allowOutOfOrderExecution - ); - - FeeTokenConfig memory feeTokenConfig = s_feeTokenConfig[message.feeToken]; - if (!feeTokenConfig.enabled) revert NotAFeeToken(message.feeToken); - - (uint224 feeTokenPrice, uint224 packedGasPrice) = - IPriceRegistry(s_dynamicConfig.priceRegistry).getTokenAndGasPrices(message.feeToken, destChainSelector); - - // Calculate premiumFee in USD with 18 decimals precision first. - // If message-only and no token transfers, a flat network fee is charged. - // If there are token transfers, premiumFee is calculated from token transfer fee. - // If there are both token transfers and message, premiumFee is only calculated from token transfer fee. - uint256 premiumFee = 0; - uint32 tokenTransferGas = 0; - uint32 tokenTransferBytesOverhead = 0; - if (message.tokenAmounts.length > 0) { - (premiumFee, tokenTransferGas, tokenTransferBytesOverhead) = - _getTokenTransferCost(message.feeToken, feeTokenPrice, message.tokenAmounts); - } else { - // Convert USD cents with 2 decimals to 18 decimals. - premiumFee = uint256(feeTokenConfig.networkFeeUSDCents) * 1e16; - } - - // Calculate data availability cost in USD with 36 decimals. Data availability cost exists on rollups that need to post - // transaction calldata onto another storage layer, e.g. Eth mainnet, incurring additional storage gas costs. - uint256 dataAvailabilityCost = 0; - // Only calculate data availability cost if data availability multiplier is non-zero. - // The multiplier should be set to 0 if destination chain does not charge data availability cost. - if (s_dynamicConfig.destDataAvailabilityMultiplierBps > 0) { - dataAvailabilityCost = _getDataAvailabilityCost( - // Parse the data availability gas price stored in the higher-order 112 bits of the encoded gas price. - uint112(packedGasPrice >> Internal.GAS_PRICE_BITS), - message.data.length, - message.tokenAmounts.length, - tokenTransferBytesOverhead - ); - } - - // Calculate execution gas fee on destination chain in USD with 36 decimals. - // We add the message gas limit, the overhead gas, the gas of passing message data to receiver, and token transfer gas together. - // We then multiply this gas total with the gas multiplier and gas price, converting it into USD with 36 decimals. - // uint112(packedGasPrice) = executionGasPrice - uint256 executionCost = uint112(packedGasPrice) - * ( - extraArgs.gasLimit + s_dynamicConfig.destGasOverhead - + (message.data.length * s_dynamicConfig.destGasPerPayloadByte) + tokenTransferGas - ) * feeTokenConfig.gasMultiplierWeiPerEth; - - // Calculate number of fee tokens to charge. - // Total USD fee is in 36 decimals, feeTokenPrice is in 18 decimals USD for 1e18 smallest token denominations. - // Result of the division is the number of smallest token denominations. - return - ((premiumFee * feeTokenConfig.premiumMultiplierWeiPerEth) + executionCost + dataAvailabilityCost) / feeTokenPrice; - } - - /// @notice Returns the estimated data availability cost of the message. - /// @dev To save on gas, we use a single destGasPerDataAvailabilityByte value for both zero and non-zero bytes. - /// @param dataAvailabilityGasPrice USD per data availability gas in 18 decimals. - /// @param messageDataLength length of the data field in the message. - /// @param numberOfTokens number of distinct token transfers in the message. - /// @param tokenTransferBytesOverhead additional token transfer data passed to destination, e.g. USDC attestation. - /// @return dataAvailabilityCostUSD36Decimal total data availability cost in USD with 36 decimals. - function _getDataAvailabilityCost( - uint112 dataAvailabilityGasPrice, - uint256 messageDataLength, - uint256 numberOfTokens, - uint32 tokenTransferBytesOverhead - ) internal view returns (uint256 dataAvailabilityCostUSD36Decimal) { - // dataAvailabilityLengthBytes sums up byte lengths of fixed message fields and dynamic message fields. - // Fixed message fields do account for the offset and length slot of the dynamic fields. - uint256 dataAvailabilityLengthBytes = Internal.MESSAGE_FIXED_BYTES + messageDataLength - + (numberOfTokens * Internal.MESSAGE_FIXED_BYTES_PER_TOKEN) + tokenTransferBytesOverhead; - - // destDataAvailabilityOverheadGas is a separate config value for flexibility to be updated independently of message cost. - // Its value is determined by CCIP lane implementation, e.g. the overhead data posted for OCR. - uint256 dataAvailabilityGas = (dataAvailabilityLengthBytes * s_dynamicConfig.destGasPerDataAvailabilityByte) - + s_dynamicConfig.destDataAvailabilityOverheadGas; - - // dataAvailabilityGasPrice is in 18 decimals, destDataAvailabilityMultiplierBps is in 4 decimals - // We pad 14 decimals to bring the result to 36 decimals, in line with token bps and execution fee. - return ((dataAvailabilityGas * dataAvailabilityGasPrice) * s_dynamicConfig.destDataAvailabilityMultiplierBps) * 1e14; - } - - /// @notice Returns the token transfer cost parameters. - /// A basis point fee is calculated from the USD value of each token transfer. - /// For each individual transfer, this fee is between [minFeeUSD, maxFeeUSD]. - /// Total transfer fee is the sum of each individual token transfer fee. - /// @dev Assumes that tokenAmounts are validated to be listed tokens elsewhere. - /// @dev Splitting one token transfer into multiple transfers is discouraged, - /// as it will result in a transferFee equal or greater than the same amount aggregated/de-duped. - /// @param feeToken address of the feeToken. - /// @param feeTokenPrice price of feeToken in USD with 18 decimals. - /// @param tokenAmounts token transfers in the message. - /// @return tokenTransferFeeUSDWei total token transfer bps fee in USD with 18 decimals. - /// @return tokenTransferGas total execution gas of the token transfers. - /// @return tokenTransferBytesOverhead additional token transfer data passed to destination, e.g. USDC attestation. - function _getTokenTransferCost( - address feeToken, - uint224 feeTokenPrice, - Client.EVMTokenAmount[] calldata tokenAmounts - ) internal view returns (uint256 tokenTransferFeeUSDWei, uint32 tokenTransferGas, uint32 tokenTransferBytesOverhead) { - uint256 numberOfTokens = tokenAmounts.length; - - for (uint256 i = 0; i < numberOfTokens; ++i) { - Client.EVMTokenAmount memory tokenAmount = tokenAmounts[i]; - - // Validate if the token is supported, do not calculate fee for unsupported tokens. - if (address(getPoolBySourceToken(i_destChainSelector, IERC20(tokenAmount.token))) == address(0)) { - revert UnsupportedToken(tokenAmount.token); - } - - TokenTransferFeeConfig memory transferFeeConfig = s_tokenTransferFeeConfig[tokenAmount.token]; - - // If the token has no specific overrides configured, we use the global defaults. - if (!transferFeeConfig.isEnabled) { - tokenTransferFeeUSDWei += uint256(s_dynamicConfig.defaultTokenFeeUSDCents) * 1e16; - tokenTransferGas += s_dynamicConfig.defaultTokenDestGasOverhead; - tokenTransferBytesOverhead += Pool.CCIP_POOL_V1_RET_BYTES; - continue; - } - - uint256 bpsFeeUSDWei = 0; - // Only calculate bps fee if ratio is greater than 0. Ratio of 0 means no bps fee for a token. - // Useful for when the PriceRegistry cannot return a valid price for the token. - if (transferFeeConfig.deciBps > 0) { - uint224 tokenPrice = 0; - if (tokenAmount.token != feeToken) { - tokenPrice = IPriceRegistry(s_dynamicConfig.priceRegistry).getValidatedTokenPrice(tokenAmount.token); - } else { - tokenPrice = feeTokenPrice; - } - - // Calculate token transfer value, then apply fee ratio - // ratio represents multiples of 0.1bps, or 1e-5 - bpsFeeUSDWei = (tokenPrice._calcUSDValueFromTokenAmount(tokenAmount.amount) * transferFeeConfig.deciBps) / 1e5; - } - - tokenTransferGas += transferFeeConfig.destGasOverhead; - tokenTransferBytesOverhead += transferFeeConfig.destBytesOverhead; - - // Bps fees should be kept within range of [minFeeUSD, maxFeeUSD]. - // Convert USD values with 2 decimals to 18 decimals. - uint256 minFeeUSDWei = uint256(transferFeeConfig.minFeeUSDCents) * 1e16; - if (bpsFeeUSDWei < minFeeUSDWei) { - tokenTransferFeeUSDWei += minFeeUSDWei; - continue; - } - - uint256 maxFeeUSDWei = uint256(transferFeeConfig.maxFeeUSDCents) * 1e16; - if (bpsFeeUSDWei > maxFeeUSDWei) { - tokenTransferFeeUSDWei += maxFeeUSDWei; - continue; - } - - tokenTransferFeeUSDWei += bpsFeeUSDWei; - } - - return (tokenTransferFeeUSDWei, tokenTransferGas, tokenTransferBytesOverhead); - } - - /// @notice Gets the fee configuration for a token - /// @param token The token to get the fee configuration for - /// @return feeTokenConfig FeeTokenConfig struct - function getFeeTokenConfig(address token) external view returns (FeeTokenConfig memory feeTokenConfig) { - return s_feeTokenConfig[token]; - } - - /// @notice Sets the fee configuration for a token - /// @param feeTokenConfigArgs Array of FeeTokenConfigArgs structs. - function setFeeTokenConfig(FeeTokenConfigArgs[] memory feeTokenConfigArgs) external { - _onlyOwnerOrAdmin(); - _setFeeTokenConfig(feeTokenConfigArgs); - } - - /// @dev Set the fee config - /// @param feeTokenConfigArgs The fee token configs. - function _setFeeTokenConfig(FeeTokenConfigArgs[] memory feeTokenConfigArgs) internal { - for (uint256 i = 0; i < feeTokenConfigArgs.length; ++i) { - FeeTokenConfigArgs memory configArg = feeTokenConfigArgs[i]; - - s_feeTokenConfig[configArg.token] = FeeTokenConfig({ - networkFeeUSDCents: configArg.networkFeeUSDCents, - gasMultiplierWeiPerEth: configArg.gasMultiplierWeiPerEth, - premiumMultiplierWeiPerEth: configArg.premiumMultiplierWeiPerEth, - enabled: configArg.enabled - }); - } - emit FeeConfigSet(feeTokenConfigArgs); - } - - /// @notice Gets the transfer fee config for a given token. - function getTokenTransferFeeConfig( - address token - ) external view returns (TokenTransferFeeConfig memory tokenTransferFeeConfig) { - return s_tokenTransferFeeConfig[token]; - } - - /// @notice Sets the transfer fee config. - /// @dev only callable by the owner or admin. - function setTokenTransferFeeConfig( - TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs, - address[] memory tokensToUseDefaultFeeConfigs - ) external { - _onlyOwnerOrAdmin(); - _setTokenTransferFeeConfig(tokenTransferFeeConfigArgs, tokensToUseDefaultFeeConfigs); - } - - /// @notice internal helper to set the token transfer fee config. - function _setTokenTransferFeeConfig( - TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs, - address[] memory tokensToUseDefaultFeeConfigs - ) internal { - for (uint256 i = 0; i < tokenTransferFeeConfigArgs.length; ++i) { - TokenTransferFeeConfigArgs memory configArg = tokenTransferFeeConfigArgs[i]; - - if (configArg.destBytesOverhead < Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) { - revert InvalidDestBytesOverhead(configArg.token, configArg.destBytesOverhead); - } - - s_tokenTransferFeeConfig[configArg.token] = TokenTransferFeeConfig({ - minFeeUSDCents: configArg.minFeeUSDCents, - maxFeeUSDCents: configArg.maxFeeUSDCents, - deciBps: configArg.deciBps, - destGasOverhead: configArg.destGasOverhead, - destBytesOverhead: configArg.destBytesOverhead, - aggregateRateLimitEnabled: configArg.aggregateRateLimitEnabled, - isEnabled: true - }); - } - emit TokenTransferFeeConfigSet(tokenTransferFeeConfigArgs); - - // Remove the custom fee configs for the tokens that are in the tokensToUseDefaultFeeConfigs array - for (uint256 i = 0; i < tokensToUseDefaultFeeConfigs.length; ++i) { - delete s_tokenTransferFeeConfig[tokensToUseDefaultFeeConfigs[i]]; - } - if (tokensToUseDefaultFeeConfigs.length > 0) { - emit TokenTransferFeeConfigDeleted(tokensToUseDefaultFeeConfigs); - } - } - - // ================================================================ - // │ NOP payments │ - // ================================================================ - - /// @notice Get the total amount of fees to be paid to the Nops (in LINK) - /// @return totalNopFees - function getNopFeesJuels() external view returns (uint96) { - return s_nopFeesJuels; - } - - /// @notice Gets the Nops and their weights - /// @return nopsAndWeights Array of NopAndWeight structs - /// @return weightsTotal The sum weight of all Nops - function getNops() external view returns (NopAndWeight[] memory nopsAndWeights, uint256 weightsTotal) { - uint256 length = s_nops.length(); - nopsAndWeights = new NopAndWeight[](length); - for (uint256 i = 0; i < length; ++i) { - (address nopAddress, uint256 nopWeight) = s_nops.at(i); - nopsAndWeights[i] = NopAndWeight({nop: nopAddress, weight: uint16(nopWeight)}); - } - weightsTotal = s_nopWeightsTotal; - return (nopsAndWeights, weightsTotal); - } - - /// @notice Sets the Nops and their weights - /// @param nopsAndWeights Array of NopAndWeight structs - function setNops(NopAndWeight[] calldata nopsAndWeights) external { - _onlyOwnerOrAdmin(); - _setNops(nopsAndWeights); - } - - /// @param nopsAndWeights New set of nops and weights - /// @dev Clears existing nops, sets new nops and weights - /// @dev We permit fees to accrue before nops are configured, in which case - /// they will go to the first set of configured nops. - function _setNops(NopAndWeight[] memory nopsAndWeights) internal { - uint256 numberOfNops = nopsAndWeights.length; - if (numberOfNops > MAX_NUMBER_OF_NOPS) revert TooManyNops(); - - // Make sure all nops have been paid before removing nops - // We only have to pay when there are nops and there is enough - // outstanding NOP balance to trigger a payment. - if (s_nopWeightsTotal > 0) { - if (s_nopFeesJuels >= s_nopWeightsTotal) { - payNops(); - } - } - - // Remove all previous nops, move from end to start to avoid shifting - for (uint256 i = s_nops.length(); i > 0; --i) { - (address nop,) = s_nops.at(i - 1); - s_nops.remove(nop); - } - - // Add new - uint32 nopWeightsTotal = 0; - // nopWeightsTotal is bounded by the MAX_NUMBER_OF_NOPS and the weight of - // a single nop being of type uint16. This ensures nopWeightsTotal will - // always fit into the uint32 type. - for (uint256 i = 0; i < numberOfNops; ++i) { - // Make sure the LINK token is not a nop because the link token doesn't allow - // self transfers. If set as nop, payNops would always revert. Since setNops - // calls payNops, we can never remove the LINK token as a nop. - address nop = nopsAndWeights[i].nop; - uint16 weight = nopsAndWeights[i].weight; - if (nop == i_linkToken || nop == address(0)) revert InvalidNopAddress(nop); - s_nops.set(nop, weight); - nopWeightsTotal += weight; - } - s_nopWeightsTotal = nopWeightsTotal; - emit NopsSet(nopWeightsTotal, nopsAndWeights); - } - - /// @notice Pays the Node Ops their outstanding balances. - /// @dev some balance can remain after payments are done. This is at most the sum - /// of the weight of all nops. Since nop weights are uint16s and we can have at - /// most MAX_NUMBER_OF_NOPS NOPs, the highest possible value is 2**22 or 0.04 gjuels. - function payNops() public { - if (msg.sender != owner()) { - if (msg.sender != s_admin) { - if (!s_nops.contains(msg.sender)) { - revert OnlyCallableByOwnerOrAdminOrNop(); - } - } - } - uint256 weightsTotal = s_nopWeightsTotal; - if (weightsTotal == 0) revert NoNopsToPay(); - - uint96 totalFeesToPay = s_nopFeesJuels; - if (totalFeesToPay < weightsTotal) revert NoFeesToPay(); - if (linkAvailableForPayment() < 0) revert InsufficientBalance(); - - uint96 fundsLeft = totalFeesToPay; - uint256 numberOfNops = s_nops.length(); - for (uint256 i = 0; i < numberOfNops; ++i) { - (address nop, uint256 weight) = s_nops.at(i); - // amount can never be higher than totalFeesToPay so the cast to uint96 is safe - uint96 amount = uint96((totalFeesToPay * weight) / weightsTotal); - fundsLeft -= amount; - IERC20(i_linkToken).safeTransfer(nop, amount); - emit NopPaid(nop, amount); - } - // Some funds can remain, since this is an incredibly small - // amount we consider this OK. - s_nopFeesJuels = fundsLeft; - } - - /// @notice Allows the owner to withdraw any ERC20 token from the contract. - /// The NOP link balance is not withdrawable. - /// @param feeToken The token to withdraw - /// @param to The address to send the tokens to - function withdrawNonLinkFees(address feeToken, address to) external { - _onlyOwnerOrAdmin(); - if (to == address(0)) revert InvalidWithdrawParams(); - - // We require the link balance to be settled before allowing withdrawal of non-link fees. - int256 linkAfterNopFees = linkAvailableForPayment(); - if (linkAfterNopFees < 0) revert LinkBalanceNotSettled(); - - if (feeToken == i_linkToken) { - // Withdraw only the left over link balance - IERC20(feeToken).safeTransfer(to, uint256(linkAfterNopFees)); - } else { - // Withdrawal all non-link tokens in the contract - IERC20(feeToken).safeTransfer(to, IERC20(feeToken).balanceOf(address(this))); - } - } - - // ================================================================ - // │ Link monitoring │ - // ================================================================ - - /// @notice Calculate remaining LINK balance after paying nops - /// @dev Allow keeper to monitor funds available for paying nops - /// @return balance if nops were to be paid - function linkAvailableForPayment() public view returns (int256) { - // Since LINK caps at uint96, casting to int256 is safe - return int256(IERC20(i_linkToken).balanceOf(address(this))) - int256(uint256(s_nopFeesJuels)); - } - - // ================================================================ - // │ Access │ - // ================================================================ - - /// @dev Require that the sender is the owner or the fee admin - /// Not a modifier to save on contract size - function _onlyOwnerOrAdmin() internal view { - if (msg.sender != owner()) { - if (msg.sender != s_admin) { - revert OnlyCallableByOwnerOrAdmin(); - } - } - } -} diff --git a/contracts/src/v0.8/ccip/pools/BurnMintTokenPoolAndProxy.sol b/contracts/src/v0.8/ccip/pools/BurnMintTokenPoolAndProxy.sol deleted file mode 100644 index 6e3fb0f479a..00000000000 --- a/contracts/src/v0.8/ccip/pools/BurnMintTokenPoolAndProxy.sol +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; -import {IBurnMintERC20} from "../../shared/token/ERC20/IBurnMintERC20.sol"; - -import {Pool} from "../libraries/Pool.sol"; -import {LegacyPoolWrapper} from "./LegacyPoolWrapper.sol"; - -contract BurnMintTokenPoolAndProxy is ITypeAndVersion, LegacyPoolWrapper { - string public constant override typeAndVersion = "BurnMintTokenPoolAndProxy 1.5.0"; - - constructor( - IBurnMintERC20 token, - address[] memory allowlist, - address rmnProxy, - address router - ) LegacyPoolWrapper(token, allowlist, rmnProxy, router) {} - - /// @notice Burn the token in the pool - /// @dev The _validateLockOrBurn check is an essential security check - function lockOrBurn( - Pool.LockOrBurnInV1 calldata lockOrBurnIn - ) external virtual override returns (Pool.LockOrBurnOutV1 memory) { - _validateLockOrBurn(lockOrBurnIn); - - if (!_hasLegacyPool()) { - IBurnMintERC20(address(i_token)).burn(lockOrBurnIn.amount); - } else { - _lockOrBurnLegacy(lockOrBurnIn); - } - - emit Burned(msg.sender, lockOrBurnIn.amount); - - return Pool.LockOrBurnOutV1({destTokenAddress: getRemoteToken(lockOrBurnIn.remoteChainSelector), destPoolData: ""}); - } - - /// @notice Mint tokens from the pool to the recipient - /// @dev The _validateReleaseOrMint check is an essential security check - function releaseOrMint( - Pool.ReleaseOrMintInV1 calldata releaseOrMintIn - ) external virtual override returns (Pool.ReleaseOrMintOutV1 memory) { - _validateReleaseOrMint(releaseOrMintIn); - - if (!_hasLegacyPool()) { - IBurnMintERC20(address(i_token)).mint(releaseOrMintIn.receiver, releaseOrMintIn.amount); - } else { - _releaseOrMintLegacy(releaseOrMintIn); - } - - emit Minted(msg.sender, releaseOrMintIn.receiver, releaseOrMintIn.amount); - - return Pool.ReleaseOrMintOutV1({destinationAmount: releaseOrMintIn.amount}); - } -} diff --git a/contracts/src/v0.8/ccip/pools/BurnWithFromMintRebasingTokenPool.sol b/contracts/src/v0.8/ccip/pools/BurnWithFromMintRebasingTokenPool.sol deleted file mode 100644 index 16acdb03b60..00000000000 --- a/contracts/src/v0.8/ccip/pools/BurnWithFromMintRebasingTokenPool.sol +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {IBurnMintERC20} from "../../shared/token/ERC20/IBurnMintERC20.sol"; - -import {Pool} from "../libraries/Pool.sol"; -import {BurnWithFromMintTokenPool} from "./BurnWithFromMintTokenPool.sol"; - -/// @notice This pool mints and burns a 3rd-party token. -/// @dev This contract is a variant of BurnMintTokenPool that uses `burn(from, amount)`. -/// @dev This contract supports minting tokens that do not mint the exact amount they are asked to mint. This can be -/// used for rebasing tokens. NOTE: for true rebasing support, the lockOrBurn method must also be updated to support -/// relaying the correct amount. -contract BurnWithFromMintRebasingTokenPool is BurnWithFromMintTokenPool { - error NegativeMintAmount(uint256 amountBurned); - - string public constant override typeAndVersion = "BurnWithFromMintRebasingTokenPool 1.5.0"; - - constructor( - IBurnMintERC20 token, - address[] memory allowlist, - address rmnProxy, - address router - ) BurnWithFromMintTokenPool(token, allowlist, rmnProxy, router) {} - - /// @notice Mint tokens from the pool to the recipient - /// @dev The _validateReleaseOrMint check is an essential security check - function releaseOrMint( - Pool.ReleaseOrMintInV1 calldata releaseOrMintIn - ) external virtual override returns (Pool.ReleaseOrMintOutV1 memory) { - _validateReleaseOrMint(releaseOrMintIn); - - uint256 balancePre = IBurnMintERC20(address(i_token)).balanceOf(releaseOrMintIn.receiver); - - // Mint to the receiver - IBurnMintERC20(address(i_token)).mint(releaseOrMintIn.receiver, releaseOrMintIn.amount); - - uint256 balancePost = IBurnMintERC20(address(i_token)).balanceOf(releaseOrMintIn.receiver); - - // Mint should not reduce the number of tokens in the receiver, if it does it will revert the call. - if (balancePost < balancePre) { - revert NegativeMintAmount(balancePre - balancePost); - } - - emit Minted(msg.sender, releaseOrMintIn.receiver, balancePost - balancePre); - - return Pool.ReleaseOrMintOutV1({destinationAmount: balancePost - balancePre}); - } -} diff --git a/contracts/src/v0.8/ccip/pools/BurnWithFromMintTokenPoolAndProxy.sol b/contracts/src/v0.8/ccip/pools/BurnWithFromMintTokenPoolAndProxy.sol deleted file mode 100644 index d9c4df0a6cc..00000000000 --- a/contracts/src/v0.8/ccip/pools/BurnWithFromMintTokenPoolAndProxy.sol +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; -import {IBurnMintERC20} from "../../shared/token/ERC20/IBurnMintERC20.sol"; - -import {Pool} from "../libraries/Pool.sol"; -import {LegacyPoolWrapper} from "./LegacyPoolWrapper.sol"; - -import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; - -contract BurnWithFromMintTokenPoolAndProxy is ITypeAndVersion, LegacyPoolWrapper { - using SafeERC20 for IBurnMintERC20; - - string public constant override typeAndVersion = "BurnWithFromMintTokenPoolAndProxy 1.5.0"; - - constructor( - IBurnMintERC20 token, - address[] memory allowlist, - address rmnProxy, - address router - ) LegacyPoolWrapper(token, allowlist, rmnProxy, router) { - // Some tokens allow burning from the sender without approval, but not all do. - // To be safe, we approve the pool to burn from the pool. - token.safeIncreaseAllowance(address(this), type(uint256).max); - } - - /// @notice Burn the token in the pool - /// @dev The _validateLockOrBurn check is an essential security check - function lockOrBurn( - Pool.LockOrBurnInV1 calldata lockOrBurnIn - ) external virtual override returns (Pool.LockOrBurnOutV1 memory) { - _validateLockOrBurn(lockOrBurnIn); - - if (!_hasLegacyPool()) { - IBurnMintERC20(address(i_token)).burnFrom(address(this), lockOrBurnIn.amount); - } else { - _lockOrBurnLegacy(lockOrBurnIn); - } - - emit Burned(msg.sender, lockOrBurnIn.amount); - - return Pool.LockOrBurnOutV1({destTokenAddress: getRemoteToken(lockOrBurnIn.remoteChainSelector), destPoolData: ""}); - } - - /// @notice Mint tokens from the pool to the recipient - /// @dev The _validateReleaseOrMint check is an essential security check - function releaseOrMint( - Pool.ReleaseOrMintInV1 calldata releaseOrMintIn - ) external virtual override returns (Pool.ReleaseOrMintOutV1 memory) { - _validateReleaseOrMint(releaseOrMintIn); - - if (!_hasLegacyPool()) { - IBurnMintERC20(address(i_token)).mint(releaseOrMintIn.receiver, releaseOrMintIn.amount); - } else { - _releaseOrMintLegacy(releaseOrMintIn); - } - - emit Minted(msg.sender, releaseOrMintIn.receiver, releaseOrMintIn.amount); - - return Pool.ReleaseOrMintOutV1({destinationAmount: releaseOrMintIn.amount}); - } -} diff --git a/contracts/src/v0.8/ccip/pools/LegacyPoolWrapper.sol b/contracts/src/v0.8/ccip/pools/LegacyPoolWrapper.sol deleted file mode 100644 index 43326681e51..00000000000 --- a/contracts/src/v0.8/ccip/pools/LegacyPoolWrapper.sol +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {IPoolPriorTo1_5} from "../interfaces/IPoolPriorTo1_5.sol"; - -import {Pool} from "../libraries/Pool.sol"; -import {TokenPool} from "./TokenPool.sol"; - -import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; - -abstract contract LegacyPoolWrapper is TokenPool { - using SafeERC20 for IERC20; - - event LegacyPoolChanged(IPoolPriorTo1_5 oldPool, IPoolPriorTo1_5 newPool); - - /// @dev The previous pool, if there is any. This is a property to make the older 1.0-1.4 pools - /// compatible with the current 1.5 pool. To achieve this, we set the previous pool address to the - /// currently deployed legacy pool. Then we configure this new pool as onRamp and offRamp on the legacy pools. - /// In the case of a 1.4 pool, this new pool contract has to be set to the Router as well, as it validates - /// who can call it through the router calls. This contract will always return itself as the only allowed ramp. - /// @dev Can be address(0), this would indicate that this pool is operating as a normal pool as opposed to - /// a proxy pool. - IPoolPriorTo1_5 internal s_previousPool; - - constructor( - IERC20 token, - address[] memory allowlist, - address rmnProxy, - address router - ) TokenPool(token, allowlist, rmnProxy, router) {} - - // ================================================================ - // │ Legacy Fallbacks │ - // ================================================================ - // Legacy fallbacks for older token pools that do not implement the new interface. - - /// @notice Legacy fallback for the 1.4 token pools. - function getOnRamp(uint64) external view returns (address onRampAddress) { - return address(this); - } - - /// @notice Return true if the given offRamp is a configured offRamp for the given source chain. - function isOffRamp(uint64 sourceChainSelector, address offRamp) external view returns (bool) { - return offRamp == address(this) || s_router.isOffRamp(sourceChainSelector, offRamp); - } - - /// @notice Configures the legacy fallback option. If the previous pool is set, this pool will act as a proxy for - /// the legacy pool. - /// @param prevPool The address of the previous pool. - function setPreviousPool(IPoolPriorTo1_5 prevPool) external onlyOwner { - IPoolPriorTo1_5 oldPrevPool = s_previousPool; - s_previousPool = prevPool; - - emit LegacyPoolChanged(oldPrevPool, prevPool); - } - - /// @notice Returns the address of the previous pool. - function getPreviousPool() external view returns (address) { - return address(s_previousPool); - } - - function _hasLegacyPool() internal view returns (bool) { - return address(s_previousPool) != address(0); - } - - function _lockOrBurnLegacy(Pool.LockOrBurnInV1 memory lockOrBurnIn) internal { - i_token.safeTransfer(address(s_previousPool), lockOrBurnIn.amount); - s_previousPool.lockOrBurn( - lockOrBurnIn.originalSender, lockOrBurnIn.receiver, lockOrBurnIn.amount, lockOrBurnIn.remoteChainSelector, "" - ); - } - - /// @notice This call converts the arguments from a >=1.5 pool call to those of a <1.5 pool call, and uses these - /// to call the previous pool. - /// @param releaseOrMintIn The 1.5 style release or mint arguments. - /// @dev Overwrites the receiver so the previous pool sends the tokens to the sender of this call, which is the - /// offRamp. This is due to the older pools sending funds directly to the receiver, while the new pools do a hop - /// through the offRamp to ensure the correct tokens are sent. - /// @dev Since extraData has never been used in LockRelease or MintBurn token pools, we can safely ignore it. - function _releaseOrMintLegacy(Pool.ReleaseOrMintInV1 memory releaseOrMintIn) internal { - s_previousPool.releaseOrMint( - releaseOrMintIn.originalSender, - releaseOrMintIn.receiver, - releaseOrMintIn.amount, - releaseOrMintIn.remoteChainSelector, - "" - ); - } -} diff --git a/contracts/src/v0.8/ccip/pools/LockReleaseTokenPoolAndProxy.sol b/contracts/src/v0.8/ccip/pools/LockReleaseTokenPoolAndProxy.sol deleted file mode 100644 index e8c39127de1..00000000000 --- a/contracts/src/v0.8/ccip/pools/LockReleaseTokenPoolAndProxy.sol +++ /dev/null @@ -1,135 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {ILiquidityContainer} from "../../liquiditymanager/interfaces/ILiquidityContainer.sol"; -import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; - -import {Pool} from "../libraries/Pool.sol"; -import {LegacyPoolWrapper} from "./LegacyPoolWrapper.sol"; - -import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; - -/// @notice Token pool used for tokens on their native chain. This uses a lock and release mechanism. -/// Because of lock/unlock requiring liquidity, this pool contract also has function to add and remove -/// liquidity. This allows for proper bookkeeping for both user and liquidity provider balances. -/// @dev One token per LockReleaseTokenPool. -contract LockReleaseTokenPoolAndProxy is LegacyPoolWrapper, ILiquidityContainer, ITypeAndVersion { - using SafeERC20 for IERC20; - - error InsufficientLiquidity(); - error LiquidityNotAccepted(); - - string public constant override typeAndVersion = "LockReleaseTokenPoolAndProxy 1.5.0"; - - /// @dev Whether or not the pool accepts liquidity. - /// External liquidity is not required when there is one canonical token deployed to a chain, - /// and CCIP is facilitating mint/burn on all the other chains, in which case the invariant - /// balanceOf(pool) on home chain >= sum(totalSupply(mint/burn "wrapped" token) on all remote chains) should always hold - bool internal immutable i_acceptLiquidity; - /// @notice The address of the rebalancer. - address internal s_rebalancer; - - constructor( - IERC20 token, - address[] memory allowlist, - address rmnProxy, - bool acceptLiquidity, - address router - ) LegacyPoolWrapper(token, allowlist, rmnProxy, router) { - i_acceptLiquidity = acceptLiquidity; - } - - /// @notice Locks the token in the pool - /// @dev The _validateLockOrBurn check is an essential security check - function lockOrBurn( - Pool.LockOrBurnInV1 calldata lockOrBurnIn - ) external virtual override returns (Pool.LockOrBurnOutV1 memory) { - _validateLockOrBurn(lockOrBurnIn); - - if (_hasLegacyPool()) { - _lockOrBurnLegacy(lockOrBurnIn); - } - - emit Locked(msg.sender, lockOrBurnIn.amount); - - return Pool.LockOrBurnOutV1({destTokenAddress: getRemoteToken(lockOrBurnIn.remoteChainSelector), destPoolData: ""}); - } - - /// @notice Release tokens from the pool to the recipient - /// @dev The _validateReleaseOrMint check is an essential security check - function releaseOrMint( - Pool.ReleaseOrMintInV1 calldata releaseOrMintIn - ) external virtual override returns (Pool.ReleaseOrMintOutV1 memory) { - _validateReleaseOrMint(releaseOrMintIn); - - if (!_hasLegacyPool()) { - // Release to the recipient - getToken().safeTransfer(releaseOrMintIn.receiver, releaseOrMintIn.amount); - } else { - _releaseOrMintLegacy(releaseOrMintIn); - } - - emit Released(msg.sender, releaseOrMintIn.receiver, releaseOrMintIn.amount); - - return Pool.ReleaseOrMintOutV1({destinationAmount: releaseOrMintIn.amount}); - } - - // @inheritdoc IERC165 - function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) { - return interfaceId == type(ILiquidityContainer).interfaceId || super.supportsInterface(interfaceId); - } - - /// @notice Gets LiquidityManager, can be address(0) if none is configured. - /// @return The current liquidity manager. - function getRebalancer() external view returns (address) { - return s_rebalancer; - } - - /// @notice Sets the LiquidityManager address. - /// @dev Only callable by the owner. - function setRebalancer(address rebalancer) external onlyOwner { - s_rebalancer = rebalancer; - } - - /// @notice Checks if the pool can accept liquidity. - /// @return true if the pool can accept liquidity, false otherwise. - function canAcceptLiquidity() external view returns (bool) { - return i_acceptLiquidity; - } - - /// @notice Adds liquidity to the pool. The tokens should be approved first. - /// @param amount The amount of liquidity to provide. - function provideLiquidity(uint256 amount) external { - if (!i_acceptLiquidity) revert LiquidityNotAccepted(); - if (s_rebalancer != msg.sender) revert Unauthorized(msg.sender); - - i_token.safeTransferFrom(msg.sender, address(this), amount); - emit LiquidityAdded(msg.sender, amount); - } - - /// @notice Removed liquidity to the pool. The tokens will be sent to msg.sender. - /// @param amount The amount of liquidity to remove. - function withdrawLiquidity(uint256 amount) external { - if (s_rebalancer != msg.sender) revert Unauthorized(msg.sender); - - if (i_token.balanceOf(address(this)) < amount) revert InsufficientLiquidity(); - i_token.safeTransfer(msg.sender, amount); - emit LiquidityRemoved(msg.sender, amount); - } - - /// @notice This function can be used to transfer liquidity from an older version of the pool to this pool. To do so - /// this pool will have to be set as the rebalancer in the older version of the pool. This allows it to transfer the - /// funds in the old pool to the new pool. - /// @dev When upgrading a LockRelease pool, this function can be called at the same time as the pool is changed in the - /// TokenAdminRegistry. This allows for a smooth transition of both liquidity and transactions to the new pool. - /// Alternatively, when no multicall is available, a portion of the funds can be transferred to the new pool before - /// changing which pool CCIP uses, to ensure both pools can operate. Then the pool should be changed in the - /// TokenAdminRegistry, which will activate the new pool. All new transactions will use the new pool and its - /// liquidity. Finally, the remaining liquidity can be transferred to the new pool using this function one more time. - /// @param from The address of the old pool. - /// @param amount The amount of liquidity to transfer. - function transferLiquidity(address from, uint256 amount) external onlyOwner { - LockReleaseTokenPoolAndProxy(from).withdrawLiquidity(amount); - } -} diff --git a/contracts/src/v0.8/ccip/ARMProxy.sol b/contracts/src/v0.8/ccip/rmn/ARMProxy.sol similarity index 92% rename from contracts/src/v0.8/ccip/ARMProxy.sol rename to contracts/src/v0.8/ccip/rmn/ARMProxy.sol index e9ccde8680b..82596af696e 100644 --- a/contracts/src/v0.8/ccip/ARMProxy.sol +++ b/contracts/src/v0.8/ccip/rmn/ARMProxy.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; -import {ITypeAndVersion} from "../shared/interfaces/ITypeAndVersion.sol"; +import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; -import {OwnerIsCreator} from "./../shared/access/OwnerIsCreator.sol"; +import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol"; /// @notice The ARMProxy serves to allow CCIP contracts /// to point to a static address for ARM queries, which saves gas @@ -16,7 +16,6 @@ contract ARMProxy is OwnerIsCreator, ITypeAndVersion { event ARMSet(address arm); // STATIC CONFIG - // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables string public constant override typeAndVersion = "ARMProxy 1.0.0"; // DYNAMIC CONFIG diff --git a/contracts/src/v0.8/ccip/test/BaseTest.t.sol b/contracts/src/v0.8/ccip/test/BaseTest.t.sol index 0c4d7d632c3..59bd3f850e9 100644 --- a/contracts/src/v0.8/ccip/test/BaseTest.t.sol +++ b/contracts/src/v0.8/ccip/test/BaseTest.t.sol @@ -35,7 +35,7 @@ contract BaseTest is Test { // Onramp uint96 internal constant MAX_NOP_FEES_JUELS = 1e27; - uint96 internal constant MAX_MSG_FEES_JUELS = 1e18; + uint96 internal constant MAX_MSG_FEES_JUELS = 1_000e18; uint32 internal constant DEST_GAS_OVERHEAD = 300_000; uint16 internal constant DEST_GAS_PER_PAYLOAD_BYTE = 16; diff --git a/contracts/src/v0.8/ccip/test/NonceManager.t.sol b/contracts/src/v0.8/ccip/test/NonceManager.t.sol index 051cf683334..0dfa2feb5ae 100644 --- a/contracts/src/v0.8/ccip/test/NonceManager.t.sol +++ b/contracts/src/v0.8/ccip/test/NonceManager.t.sol @@ -1,21 +1,23 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; -import {NonceManager} from "../NonceManager.sol"; import {ICommitStore} from "../interfaces/ICommitStore.sol"; +import {IEVM2AnyOnRamp} from "../interfaces/IEVM2AnyOnRamp.sol"; + +import {NonceManager} from "../NonceManager.sol"; +import {Router} from "../Router.sol"; import {Client} from "../libraries/Client.sol"; import {Internal} from "../libraries/Internal.sol"; import {Pool} from "../libraries/Pool.sol"; import {RateLimiter} from "../libraries/RateLimiter.sol"; import {OffRamp} from "../offRamp/OffRamp.sol"; -import {EVM2EVMOnRamp} from "../onRamp/EVM2EVMOnRamp.sol"; import {OnRamp} from "../onRamp/OnRamp.sol"; import {BaseTest} from "./BaseTest.t.sol"; import {EVM2EVMOffRampHelper} from "./helpers/EVM2EVMOffRampHelper.sol"; -import {EVM2EVMOnRampHelper} from "./helpers/EVM2EVMOnRampHelper.sol"; -import {MockCommitStore} from "./mocks/MockCommitStore.sol"; +import {OnRampHelper} from "./helpers/OnRampHelper.sol"; import {OffRampSetup} from "./offRamp/OffRampSetup.t.sol"; import {OnRampSetup} from "./onRamp/OnRampSetup.t.sol"; + import {Test} from "forge-std/Test.sol"; contract NonceManager_typeAndVersion is Test { @@ -190,65 +192,18 @@ contract NonceManager_applyPreviousRampsUpdates is OnRampSetup { contract NonceManager_OnRampUpgrade is OnRampSetup { uint256 internal constant FEE_AMOUNT = 1234567890; - EVM2EVMOnRampHelper internal s_prevOnRamp; + OnRampHelper internal s_prevOnRamp; function setUp() public virtual override { super.setUp(); - EVM2EVMOnRamp.FeeTokenConfigArgs[] memory feeTokenConfigArgs = new EVM2EVMOnRamp.FeeTokenConfigArgs[](1); - feeTokenConfigArgs[0] = EVM2EVMOnRamp.FeeTokenConfigArgs({ - token: s_sourceFeeToken, - networkFeeUSDCents: 1_00, // 1 USD - gasMultiplierWeiPerEth: 1e18, // 1x - premiumMultiplierWeiPerEth: 5e17, // 0.5x - enabled: true - }); - - EVM2EVMOnRamp.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfig = - new EVM2EVMOnRamp.TokenTransferFeeConfigArgs[](1); - - tokenTransferFeeConfig[0] = EVM2EVMOnRamp.TokenTransferFeeConfigArgs({ - token: s_sourceFeeToken, - minFeeUSDCents: 1_00, // 1 USD - maxFeeUSDCents: 1000_00, // 1,000 USD - deciBps: 2_5, // 2.5 bps, or 0.025% - destGasOverhead: 140_000, - destBytesOverhead: uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES), - aggregateRateLimitEnabled: true - }); - - s_prevOnRamp = new EVM2EVMOnRampHelper( - EVM2EVMOnRamp.StaticConfig({ - linkToken: s_sourceTokens[0], - chainSelector: SOURCE_CHAIN_SELECTOR, - destChainSelector: DEST_CHAIN_SELECTOR, - defaultTxGasLimit: GAS_LIMIT, - maxNopFeesJuels: MAX_NOP_FEES_JUELS, - prevOnRamp: address(0), - rmnProxy: address(s_mockRMN), - tokenAdminRegistry: address(s_tokenAdminRegistry) - }), - EVM2EVMOnRamp.DynamicConfig({ - router: address(s_sourceRouter), - maxNumberOfTokensPerMsg: MAX_TOKENS_LENGTH, - destGasOverhead: DEST_GAS_OVERHEAD, - destGasPerPayloadByte: DEST_GAS_PER_PAYLOAD_BYTE, - destDataAvailabilityOverheadGas: DEST_DATA_AVAILABILITY_OVERHEAD_GAS, - destGasPerDataAvailabilityByte: DEST_GAS_PER_DATA_AVAILABILITY_BYTE, - destDataAvailabilityMultiplierBps: DEST_GAS_DATA_AVAILABILITY_MULTIPLIER_BPS, - priceRegistry: address(s_feeQuoter), - maxDataBytes: MAX_DATA_SIZE, - maxPerMsgGasLimit: MAX_GAS_LIMIT, - defaultTokenFeeUSDCents: DEFAULT_TOKEN_FEE_USD_CENTS, - defaultTokenDestGasOverhead: DEFAULT_TOKEN_DEST_GAS_OVERHEAD, - enforceOutOfOrder: false - }), - RateLimiter.Config({isEnabled: true, capacity: 100e28, rate: 1e15}), - feeTokenConfigArgs, - tokenTransferFeeConfig, - new EVM2EVMOnRamp.NopAndWeight[](0) + (s_prevOnRamp,) = _deployOnRamp( + SOURCE_CHAIN_SELECTOR, s_sourceRouter, address(s_outboundNonceManager), address(s_tokenAdminRegistry) ); + // Since the previous onRamp is not a 1.5 ramp it doesn't have the getSenderNonce function. We mock it to return 0 + vm.mockCall(address(s_prevOnRamp), abi.encodeWithSelector(IEVM2AnyOnRamp.getSenderNonce.selector), abi.encode(0)); + NonceManager.PreviousRampsArgs[] memory previousRamps = new NonceManager.PreviousRampsArgs[](1); previousRamps[0] = NonceManager.PreviousRampsArgs(DEST_CHAIN_SELECTOR, NonceManager.PreviousRamps(address(s_prevOnRamp), address(0))); @@ -266,6 +221,7 @@ contract NonceManager_OnRampUpgrade is OnRampSetup { vm.expectEmit(); emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, FEE_AMOUNT, OWNER)); + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, FEE_AMOUNT, OWNER); } @@ -315,13 +271,13 @@ contract NonceManager_OnRampUpgrade is OnRampSetup { // new onramp nonce should start from 1 for new sender vm.expectEmit(); emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, FEE_AMOUNT, newSender)); + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, FEE_AMOUNT, newSender); } } contract NonceManager_OffRampUpgrade is OffRampSetup { EVM2EVMOffRampHelper internal s_prevOffRamp; - EVM2EVMOffRampHelper[] internal s_nestedPrevOffRamps; address internal constant SINGLE_LANE_ON_RAMP_ADDRESS_1 = abi.decode(ON_RAMP_ADDRESS_1, (address)); address internal constant SINGLE_LANE_ON_RAMP_ADDRESS_2 = abi.decode(ON_RAMP_ADDRESS_2, (address)); @@ -330,33 +286,13 @@ contract NonceManager_OffRampUpgrade is OffRampSetup { function setUp() public virtual override { super.setUp(); - ICommitStore mockPrevCommitStore = new MockCommitStore(); - s_prevOffRamp = _deploySingleLaneOffRamp( - mockPrevCommitStore, s_destRouter, address(0), SOURCE_CHAIN_SELECTOR_1, SINGLE_LANE_ON_RAMP_ADDRESS_1 - ); - - s_nestedPrevOffRamps = new EVM2EVMOffRampHelper[](2); - s_nestedPrevOffRamps[0] = _deploySingleLaneOffRamp( - mockPrevCommitStore, s_destRouter, address(0), SOURCE_CHAIN_SELECTOR_2, SINGLE_LANE_ON_RAMP_ADDRESS_2 - ); - s_nestedPrevOffRamps[1] = _deploySingleLaneOffRamp( - mockPrevCommitStore, - s_destRouter, - address(s_nestedPrevOffRamps[0]), - SOURCE_CHAIN_SELECTOR_2, - SINGLE_LANE_ON_RAMP_ADDRESS_2 - ); + s_prevOffRamp = new EVM2EVMOffRampHelper(); - NonceManager.PreviousRampsArgs[] memory previousRamps = new NonceManager.PreviousRampsArgs[](3); + NonceManager.PreviousRampsArgs[] memory previousRamps = new NonceManager.PreviousRampsArgs[](1); previousRamps[0] = NonceManager.PreviousRampsArgs( SOURCE_CHAIN_SELECTOR_1, NonceManager.PreviousRamps(address(0), address(s_prevOffRamp)) ); - previousRamps[1] = NonceManager.PreviousRampsArgs( - SOURCE_CHAIN_SELECTOR_2, NonceManager.PreviousRamps(address(0), address(s_nestedPrevOffRamps[1])) - ); - previousRamps[2] = NonceManager.PreviousRampsArgs( - SOURCE_CHAIN_SELECTOR_3, NonceManager.PreviousRamps(SINGLE_LANE_ON_RAMP_ADDRESS_3, address(0)) - ); + s_inboundNonceManager.applyPreviousRampsUpdates(previousRamps); OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](3); @@ -404,18 +340,14 @@ contract NonceManager_OffRampUpgrade is OffRampSetup { } function test_NoPrevOffRampForChain_Success() public { - Internal.EVM2EVMMessage[] memory messages = - _generateSingleLaneSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, SINGLE_LANE_ON_RAMP_ADDRESS_1); - uint64 startNonceChain3 = - s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, abi.encode(messages[0].sender)); - s_prevOffRamp.execute( - _generateSingleLaneRampReportFromMessages(messages), new EVM2EVMOffRampHelper.GasLimitOverride[](0) - ); + address[] memory senders = new address[](1); + senders[0] = OWNER; + + uint64 startNonceChain3 = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, abi.encode(senders[0])); + s_prevOffRamp.execute(senders); // Nonce unchanged for chain 3 - assertEq( - startNonceChain3, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, abi.encode(messages[0].sender)) - ); + assertEq(startNonceChain3, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, abi.encode(senders[0]))); Internal.Any2EVMRampMessage[] memory messagesChain3 = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3); @@ -440,60 +372,26 @@ contract NonceManager_OffRampUpgrade is OffRampSetup { } function test_UpgradedSenderNoncesReadsPreviousRamp_Success() public { - Internal.EVM2EVMMessage[] memory messages = - _generateSingleLaneSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, SINGLE_LANE_ON_RAMP_ADDRESS_1); - uint64 startNonce = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messages[0].sender)); + address[] memory senders = new address[](1); + senders[0] = OWNER; - for (uint64 i = 1; i < 4; ++i) { - s_prevOffRamp.execute( - _generateSingleLaneRampReportFromMessages(messages), new EVM2EVMOffRampHelper.GasLimitOverride[](0) - ); - - // messages contains a single message - update for the next execution - messages[0].nonce++; - messages[0].sequenceNumber++; - messages[0].messageId = Internal._hash(messages[0], s_prevOffRamp.metadataHash()); - - assertEq( - startNonce + i, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messages[0].sender)) - ); - } - } - - function test_UpgradedSenderNoncesReadsPreviousRampTransitive_Success() public { - Internal.EVM2EVMMessage[] memory messages = - _generateSingleLaneSingleBasicMessage(SOURCE_CHAIN_SELECTOR_2, SINGLE_LANE_ON_RAMP_ADDRESS_2); - uint64 startNonce = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_2, abi.encode(messages[0].sender)); + uint64 startNonce = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(senders[0])); for (uint64 i = 1; i < 4; ++i) { - s_nestedPrevOffRamps[0].execute( - _generateSingleLaneRampReportFromMessages(messages), new EVM2EVMOffRampHelper.GasLimitOverride[](0) - ); - - // messages contains a single message - update for the next execution - messages[0].nonce++; - messages[0].sequenceNumber++; - messages[0].messageId = Internal._hash(messages[0], s_nestedPrevOffRamps[0].metadataHash()); - - // Read through prev sender nonce through prevOffRamp -> prevPrevOffRamp - assertEq( - startNonce + i, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_2, abi.encode(messages[0].sender)) - ); + s_prevOffRamp.execute(senders); + + assertEq(startNonce + i, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(senders[0]))); } } function test_UpgradedNonceStartsAtV1Nonce_Success() public { - Internal.EVM2EVMMessage[] memory messages = - _generateSingleLaneSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, SINGLE_LANE_ON_RAMP_ADDRESS_1); + address[] memory senders = new address[](1); + senders[0] = OWNER; - uint64 startNonce = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messages[0].sender)); - s_prevOffRamp.execute( - _generateSingleLaneRampReportFromMessages(messages), new EVM2EVMOffRampHelper.GasLimitOverride[](0) - ); + uint64 startNonce = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(senders[0])); + s_prevOffRamp.execute(senders); - assertEq( - startNonce + 1, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messages[0].sender)) - ); + assertEq(startNonce + 1, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(senders[0]))); Internal.Any2EVMRampMessage[] memory messagesMultiRamp = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); @@ -543,12 +441,10 @@ contract NonceManager_OffRampUpgrade is OffRampSetup { } function test_UpgradedNonceNewSenderStartsAtZero_Success() public { - Internal.EVM2EVMMessage[] memory messages = - _generateSingleLaneSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, SINGLE_LANE_ON_RAMP_ADDRESS_1); + address[] memory senders = new address[](1); + senders[0] = OWNER; - s_prevOffRamp.execute( - _generateSingleLaneRampReportFromMessages(messages), new EVM2EVMOffRampHelper.GasLimitOverride[](0) - ); + s_prevOffRamp.execute(senders); Internal.Any2EVMRampMessage[] memory messagesMultiRamp = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); @@ -594,21 +490,12 @@ contract NonceManager_OffRampUpgrade is OffRampSetup { ); assertEq(startNonce, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender)); - Internal.EVM2EVMMessage[] memory messagesSingleLane = - _generateSingleLaneSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, SINGLE_LANE_ON_RAMP_ADDRESS_1); - - messagesSingleLane[0].nonce = 1; - messagesSingleLane[0].sender = newSender; - messagesSingleLane[0].messageId = Internal._hash(messagesSingleLane[0], s_prevOffRamp.metadataHash()); + address[] memory senders = new address[](1); + senders[0] = newSender; // previous offramp executes msg and increases nonce - s_prevOffRamp.execute( - _generateSingleLaneRampReportFromMessages(messagesSingleLane), new EVM2EVMOffRampHelper.GasLimitOverride[](0) - ); - assertEq( - startNonce + 1, - s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(messagesSingleLane[0].sender)) - ); + s_prevOffRamp.execute(senders); + assertEq(startNonce + 1, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(senders[0]))); messages[0].header.nonce = 2; messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); @@ -618,6 +505,7 @@ contract NonceManager_OffRampUpgrade is OffRampSetup { s_offRamp.executeSingleReport( _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) ); + assertExecutionStateChangedEventLogs( SOURCE_CHAIN_SELECTOR_1, messages[0].header.sequenceNumber, @@ -626,54 +514,7 @@ contract NonceManager_OffRampUpgrade is OffRampSetup { Internal.MessageExecutionState.SUCCESS, "" ); - assertEq(startNonce + 2, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender)); - } - - function _generateSingleLaneRampReportFromMessages( - Internal.EVM2EVMMessage[] memory messages - ) internal pure returns (Internal.ExecutionReport memory) { - bytes[][] memory offchainTokenData = new bytes[][](messages.length); - - for (uint256 i = 0; i < messages.length; ++i) { - offchainTokenData[i] = new bytes[](messages[i].tokenAmounts.length); - } - - return Internal.ExecutionReport({ - proofs: new bytes32[](0), - proofFlagBits: 2 ** 256 - 1, - messages: messages, - offchainTokenData: offchainTokenData - }); - } - - function _generateSingleLaneSingleBasicMessage( - uint64 sourceChainSelector, - address onRamp - ) internal view returns (Internal.EVM2EVMMessage[] memory) { - Internal.EVM2EVMMessage[] memory messages = new Internal.EVM2EVMMessage[](1); - - bytes memory data = abi.encode(0); - messages[0] = Internal.EVM2EVMMessage({ - sequenceNumber: 1, - sender: OWNER, - nonce: 1, - gasLimit: GAS_LIMIT, - strict: false, - sourceChainSelector: sourceChainSelector, - receiver: address(s_receiver), - data: data, - tokenAmounts: new Client.EVMTokenAmount[](0), - sourceTokenData: new bytes[](0), - feeToken: s_destFeeToken, - feeTokenAmount: uint256(0), - messageId: "" - }); - messages[0].messageId = Internal._hash( - messages[0], - keccak256(abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, sourceChainSelector, DEST_CHAIN_SELECTOR, onRamp)) - ); - - return messages; + assertEq(startNonce + 2, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender)); } } diff --git a/contracts/src/v0.8/ccip/test/applications/DefensiveExample.t.sol b/contracts/src/v0.8/ccip/test/applications/DefensiveExample.t.sol index 18453f9f525..c68907bb9f9 100644 --- a/contracts/src/v0.8/ccip/test/applications/DefensiveExample.t.sol +++ b/contracts/src/v0.8/ccip/test/applications/DefensiveExample.t.sol @@ -3,11 +3,11 @@ pragma solidity ^0.8.0; import {DefensiveExample} from "../../applications/DefensiveExample.sol"; import {Client} from "../../libraries/Client.sol"; -import {EVM2EVMOnRampSetup} from "../onRamp/EVM2EVMOnRampSetup.t.sol"; +import {OnRampSetup} from "../onRamp/OnRampSetup.t.sol"; import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -contract DefensiveExampleTest is EVM2EVMOnRampSetup { +contract DefensiveExampleTest is OnRampSetup { event MessageFailed(bytes32 indexed messageId, bytes reason); event MessageSucceeded(bytes32 indexed messageId); event MessageRecovered(bytes32 indexed messageId); @@ -16,7 +16,7 @@ contract DefensiveExampleTest is EVM2EVMOnRampSetup { uint64 internal sourceChainSelector = 7331; function setUp() public virtual override { - EVM2EVMOnRampSetup.setUp(); + super.setUp(); s_receiver = new DefensiveExample(s_destRouter, IERC20(s_destFeeToken)); s_receiver.enableChain(sourceChainSelector, abi.encode("")); diff --git a/contracts/src/v0.8/ccip/test/applications/ImmutableExample.t.sol b/contracts/src/v0.8/ccip/test/applications/ImmutableExample.t.sol index 0fb47b1f9b5..61b0204e7d8 100644 --- a/contracts/src/v0.8/ccip/test/applications/ImmutableExample.t.sol +++ b/contracts/src/v0.8/ccip/test/applications/ImmutableExample.t.sol @@ -4,13 +4,13 @@ import {IAny2EVMMessageReceiver} from "../../interfaces/IAny2EVMMessageReceiver. import {CCIPClientExample} from "../../applications/CCIPClientExample.sol"; import {Client} from "../../libraries/Client.sol"; -import {EVM2EVMOnRampSetup} from "../onRamp/EVM2EVMOnRampSetup.t.sol"; +import {OnRampSetup} from "../onRamp/OnRampSetup.t.sol"; import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; import {ERC165Checker} from "../../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/introspection/ERC165Checker.sol"; -contract CCIPClientExample_sanity is EVM2EVMOnRampSetup { +contract CCIPClientExample_sanity is OnRampSetup { function test_ImmutableExamples_Success() public { CCIPClientExample exampleContract = new CCIPClientExample(s_sourceRouter, IERC20(s_sourceFeeToken)); deal(address(exampleContract), 100 ether); diff --git a/contracts/src/v0.8/ccip/test/applications/PingPongDemo.t.sol b/contracts/src/v0.8/ccip/test/applications/PingPongDemo.t.sol index f253a72fcb3..7bd5aaea9d6 100644 --- a/contracts/src/v0.8/ccip/test/applications/PingPongDemo.t.sol +++ b/contracts/src/v0.8/ccip/test/applications/PingPongDemo.t.sol @@ -3,17 +3,17 @@ pragma solidity 0.8.24; import {PingPongDemo} from "../../applications/PingPongDemo.sol"; import {Client} from "../../libraries/Client.sol"; -import "../onRamp/EVM2EVMOnRampSetup.t.sol"; +import "../onRamp/OnRampSetup.t.sol"; // setup -contract PingPongDappSetup is EVM2EVMOnRampSetup { +contract PingPongDappSetup is OnRampSetup { PingPongDemo internal s_pingPong; IERC20 internal s_feeToken; address internal immutable i_pongContract = makeAddr("ping_pong_counterpart"); function setUp() public virtual override { - EVM2EVMOnRampSetup.setUp(); + super.setUp(); s_feeToken = IERC20(s_sourceTokens[0]); s_pingPong = new PingPongDemo(address(s_sourceRouter), s_feeToken); @@ -30,75 +30,23 @@ contract PingPong_startPingPong is PingPongDappSetup { uint256 internal pingPongNumber = 1; function test_StartPingPong_With_Sequenced_Ordered_Success() public { - Client.EVM2AnyMessage memory sentMessage = Client.EVM2AnyMessage({ - receiver: abi.encode(i_pongContract), - data: abi.encode(pingPongNumber), - tokenAmounts: new Client.EVMTokenAmount[](0), - feeToken: s_sourceFeeToken, - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 200_000})) - }); - - uint256 expectedFee = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, sentMessage); - - Internal.EVM2EVMMessage memory message = Internal.EVM2EVMMessage({ - sequenceNumber: 1, - feeTokenAmount: expectedFee, - sourceChainSelector: SOURCE_CHAIN_SELECTOR, - sender: address(s_pingPong), - receiver: i_pongContract, - nonce: 1, - data: abi.encode(pingPongNumber), - tokenAmounts: sentMessage.tokenAmounts, - sourceTokenData: new bytes[](sentMessage.tokenAmounts.length), - gasLimit: 200_000, - feeToken: sentMessage.feeToken, - strict: false, - messageId: "" - }); - - _assertPingPongSuccess(message); + _assertPingPongSuccess(); } function test_StartPingPong_With_OOO_Success() public { s_pingPong.setOutOfOrderExecution(true); - Client.EVM2AnyMessage memory sentMessage = Client.EVM2AnyMessage({ - receiver: abi.encode(i_pongContract), - data: abi.encode(pingPongNumber), - tokenAmounts: new Client.EVMTokenAmount[](0), - feeToken: s_sourceFeeToken, - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV2({gasLimit: 200_000, allowOutOfOrderExecution: true})) - }); - - uint256 expectedFee = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, sentMessage); - - Internal.EVM2EVMMessage memory message = Internal.EVM2EVMMessage({ - sequenceNumber: 1, - feeTokenAmount: expectedFee, - sourceChainSelector: SOURCE_CHAIN_SELECTOR, - sender: address(s_pingPong), - receiver: i_pongContract, - nonce: 0, - data: abi.encode(pingPongNumber), - tokenAmounts: sentMessage.tokenAmounts, - sourceTokenData: new bytes[](sentMessage.tokenAmounts.length), - gasLimit: 200_000, - feeToken: sentMessage.feeToken, - strict: false, - messageId: "" - }); - - _assertPingPongSuccess(message); + _assertPingPongSuccess(); } - function _assertPingPongSuccess(Internal.EVM2EVMMessage memory message) internal { - message.messageId = Internal._hash(message, s_metadataHash); - + function _assertPingPongSuccess() internal { vm.expectEmit(); emit PingPongDemo.Ping(pingPongNumber); - vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(message); + Internal.EVM2AnyRampMessage memory message; + + vm.expectEmit(false, false, false, false); + emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, message); s_pingPong.startPingPong(); } diff --git a/contracts/src/v0.8/ccip/test/applications/SelfFundedPingPong.t.sol b/contracts/src/v0.8/ccip/test/applications/SelfFundedPingPong.t.sol deleted file mode 100644 index d5db9d1f9d0..00000000000 --- a/contracts/src/v0.8/ccip/test/applications/SelfFundedPingPong.t.sol +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {SelfFundedPingPong} from "../../applications/SelfFundedPingPong.sol"; -import {Client} from "../../libraries/Client.sol"; -import {EVM2EVMOnRamp} from "../../onRamp/EVM2EVMOnRamp.sol"; -import {EVM2EVMOnRampSetup} from "../onRamp/EVM2EVMOnRampSetup.t.sol"; - -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; - -contract SelfFundedPingPongDappSetup is EVM2EVMOnRampSetup { - SelfFundedPingPong internal s_pingPong; - IERC20 internal s_feeToken; - uint8 internal constant s_roundTripsBeforeFunding = 0; - - address internal immutable i_pongContract = makeAddr("ping_pong_counterpart"); - - function setUp() public virtual override { - EVM2EVMOnRampSetup.setUp(); - - s_feeToken = IERC20(s_sourceTokens[0]); - s_pingPong = new SelfFundedPingPong(address(s_sourceRouter), s_feeToken, s_roundTripsBeforeFunding); - s_pingPong.setCounterpart(DEST_CHAIN_SELECTOR, i_pongContract); - - uint256 fundingAmount = 5e18; - - // set ping pong as an onRamp nop to make sure that funding runs - EVM2EVMOnRamp.NopAndWeight[] memory nopsAndWeights = new EVM2EVMOnRamp.NopAndWeight[](1); - nopsAndWeights[0] = EVM2EVMOnRamp.NopAndWeight({nop: address(s_pingPong), weight: 1}); - s_onRamp.setNops(nopsAndWeights); - - // Fund the contract with LINK tokens - s_feeToken.transfer(address(s_pingPong), fundingAmount); - } -} - -contract SelfFundedPingPong_ccipReceive is SelfFundedPingPongDappSetup { - function test_Funding_Success() public { - Client.Any2EVMMessage memory message = Client.Any2EVMMessage({ - messageId: keccak256("msg id"), - sourceChainSelector: DEST_CHAIN_SELECTOR, - sender: abi.encode(i_pongContract), - data: "", - destTokenAmounts: new Client.EVMTokenAmount[](0) - }); - - uint8 countIncrBeforeFunding = 5; - - vm.expectEmit(); - emit SelfFundedPingPong.CountIncrBeforeFundingSet(countIncrBeforeFunding); - - s_pingPong.setCountIncrBeforeFunding(countIncrBeforeFunding); - - vm.startPrank(address(s_sourceRouter)); - for (uint256 pingPongNumber = 0; pingPongNumber <= countIncrBeforeFunding; ++pingPongNumber) { - message.data = abi.encode(pingPongNumber); - if (pingPongNumber == countIncrBeforeFunding - 1) { - vm.expectEmit(); - emit SelfFundedPingPong.Funded(); - vm.expectCall(address(s_onRamp), ""); - } - s_pingPong.ccipReceive(message); - } - } - - function test_FundingIfNotANop_Revert() public { - EVM2EVMOnRamp.NopAndWeight[] memory nopsAndWeights = new EVM2EVMOnRamp.NopAndWeight[](0); - s_onRamp.setNops(nopsAndWeights); - - uint8 countIncrBeforeFunding = 3; - s_pingPong.setCountIncrBeforeFunding(countIncrBeforeFunding); - - vm.startPrank(address(s_sourceRouter)); - Client.Any2EVMMessage memory message = Client.Any2EVMMessage({ - messageId: bytes32("a"), - sourceChainSelector: DEST_CHAIN_SELECTOR, - sender: abi.encode(i_pongContract), - data: abi.encode(countIncrBeforeFunding), - destTokenAmounts: new Client.EVMTokenAmount[](0) - }); - - // because pingPong is not set as a nop - vm.expectRevert(EVM2EVMOnRamp.OnlyCallableByOwnerOrAdminOrNop.selector); - s_pingPong.ccipReceive(message); - } -} - -contract SelfFundedPingPong_setCountIncrBeforeFunding is SelfFundedPingPongDappSetup { - function test_setCountIncrBeforeFunding() public { - uint8 c = s_pingPong.getCountIncrBeforeFunding(); - - vm.expectEmit(); - emit SelfFundedPingPong.CountIncrBeforeFundingSet(c + 1); - - s_pingPong.setCountIncrBeforeFunding(c + 1); - uint8 c2 = s_pingPong.getCountIncrBeforeFunding(); - assertEq(c2, c + 1); - } -} diff --git a/contracts/src/v0.8/ccip/test/applications/TokenProxy.t.sol b/contracts/src/v0.8/ccip/test/applications/TokenProxy.t.sol deleted file mode 100644 index 9e78f6e369f..00000000000 --- a/contracts/src/v0.8/ccip/test/applications/TokenProxy.t.sol +++ /dev/null @@ -1,211 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.24; - -import {TokenProxy} from "../../applications/TokenProxy.sol"; -import {Client} from "../../libraries/Client.sol"; -import {Internal} from "../../libraries/Internal.sol"; -import {EVM2EVMOnRamp} from "../../onRamp/EVM2EVMOnRamp.sol"; -import {EVM2EVMOnRampSetup} from "../onRamp/EVM2EVMOnRampSetup.t.sol"; - -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; - -contract TokenProxySetup is EVM2EVMOnRampSetup { - TokenProxy internal s_tokenProxy; - IERC20 internal s_feeToken; - IERC20 internal s_transferToken; - - function setUp() public virtual override { - EVM2EVMOnRampSetup.setUp(); - - s_feeToken = IERC20(s_sourceTokens[0]); - s_transferToken = IERC20(s_sourceTokens[1]); - s_tokenProxy = new TokenProxy(address(s_sourceRouter), address(s_transferToken)); - - s_transferToken.approve(address(s_tokenProxy), type(uint256).max); - s_feeToken.approve(address(s_tokenProxy), type(uint256).max); - } -} - -contract TokenProxy_constructor is TokenProxySetup { - function test_Constructor() public view { - assertEq(address(s_tokenProxy.getRouter()), address(s_sourceRouter)); - assertEq(address(s_tokenProxy.getToken()), address(s_transferToken)); - } -} - -contract TokenProxy_getFee is TokenProxySetup { - function test_GetFee_Success() public view { - Client.EVMTokenAmount[] memory tokens = new Client.EVMTokenAmount[](1); - tokens[0] = Client.EVMTokenAmount({token: address(s_transferToken), amount: 1e18}); - - Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ - receiver: abi.encode(s_tokenProxy), - data: "", - tokenAmounts: tokens, - feeToken: s_sourceFeeToken, - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 0})) - }); - - uint256 expectedFee = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, message); - uint256 actualFee = s_tokenProxy.getFee(DEST_CHAIN_SELECTOR, message); - assertEq(expectedFee, actualFee); - } - - // Reverts - - function test_GetFeeInvalidToken_Revert() public { - Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ - receiver: abi.encode(s_tokenProxy), - data: "", - tokenAmounts: new Client.EVMTokenAmount[](0), - feeToken: s_sourceFeeToken, - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 0})) - }); - - vm.expectRevert(TokenProxy.InvalidToken.selector); - - s_tokenProxy.getFee(DEST_CHAIN_SELECTOR, message); - } - - function test_GetFeeNoDataAllowed_Revert() public { - Client.EVMTokenAmount[] memory tokens = new Client.EVMTokenAmount[](1); - tokens[0] = Client.EVMTokenAmount({token: address(s_transferToken), amount: 1e18}); - - Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ - receiver: abi.encode(s_tokenProxy), - data: "not empty", - tokenAmounts: tokens, - feeToken: s_sourceFeeToken, - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 0})) - }); - - vm.expectRevert(TokenProxy.NoDataAllowed.selector); - - s_tokenProxy.getFee(DEST_CHAIN_SELECTOR, message); - } - - function test_GetFeeGasShouldBeZero_Revert() public { - Client.EVMTokenAmount[] memory tokens = new Client.EVMTokenAmount[](1); - tokens[0] = Client.EVMTokenAmount({token: address(s_transferToken), amount: 1e18}); - - Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ - receiver: abi.encode(s_tokenProxy), - data: "", - tokenAmounts: tokens, - feeToken: s_sourceFeeToken, - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 10})) - }); - - vm.expectRevert(TokenProxy.GasShouldBeZero.selector); - - s_tokenProxy.getFee(DEST_CHAIN_SELECTOR, message); - } -} - -contract TokenProxy_ccipSend is TokenProxySetup { - function test_CcipSend_Success() public { - vm.pauseGasMetering(); - Client.EVMTokenAmount[] memory tokens = new Client.EVMTokenAmount[](1); - tokens[0] = Client.EVMTokenAmount({token: address(s_transferToken), amount: 1e18}); - - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.tokenAmounts = tokens; - message.extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 0})); - - uint256 expectedFee = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, message); - - s_feeToken.approve(address(s_tokenProxy), expectedFee); - - Internal.EVM2EVMMessage memory msgEvent = _messageToEvent(message, 1, 1, expectedFee, OWNER); - msgEvent.sender = address(s_tokenProxy); - msgEvent.messageId = Internal._hash(msgEvent, s_metadataHash); - - vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(msgEvent); - - vm.resumeGasMetering(); - s_tokenProxy.ccipSend(DEST_CHAIN_SELECTOR, message); - } - - function test_CcipSendNative_Success() public { - vm.pauseGasMetering(); - Client.EVMTokenAmount[] memory tokens = new Client.EVMTokenAmount[](1); - tokens[0] = Client.EVMTokenAmount({token: address(s_transferToken), amount: 1e18}); - - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.tokenAmounts = tokens; - message.feeToken = address(0); - message.extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 0})); - - uint256 expectedFee = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, message); - - Internal.EVM2EVMMessage memory msgEvent = _messageToEvent(message, 1, 1, expectedFee, OWNER); - msgEvent.sender = address(s_tokenProxy); - msgEvent.feeToken = s_sourceRouter.getWrappedNative(); - msgEvent.messageId = Internal._hash(msgEvent, s_metadataHash); - - vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(msgEvent); - - vm.resumeGasMetering(); - s_tokenProxy.ccipSend{value: expectedFee}(DEST_CHAIN_SELECTOR, message); - } - - // Reverts - - function test_CcipSendInsufficientAllowance_Revert() public { - Client.EVMTokenAmount[] memory tokens = new Client.EVMTokenAmount[](1); - tokens[0] = Client.EVMTokenAmount({token: address(s_transferToken), amount: 1e18}); - - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.tokenAmounts = tokens; - message.extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 0})); - - // Revoke allowance - s_transferToken.approve(address(s_tokenProxy), 0); - - vm.expectRevert("ERC20: insufficient allowance"); - - s_tokenProxy.ccipSend(DEST_CHAIN_SELECTOR, message); - } - - function test_CcipSendInvalidToken_Revert() public { - Client.EVMTokenAmount[] memory tokens = new Client.EVMTokenAmount[](1); - tokens[0] = Client.EVMTokenAmount({token: address(s_feeToken), amount: 1e18}); - - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.tokenAmounts = tokens; - message.extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 0})); - - vm.expectRevert(TokenProxy.InvalidToken.selector); - - s_tokenProxy.ccipSend(DEST_CHAIN_SELECTOR, message); - } - - function test_CcipSendNoDataAllowed_Revert() public { - Client.EVMTokenAmount[] memory tokens = new Client.EVMTokenAmount[](1); - tokens[0] = Client.EVMTokenAmount({token: address(s_transferToken), amount: 1e18}); - - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.tokenAmounts = tokens; - message.data = "not empty"; - message.extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 0})); - - vm.expectRevert(TokenProxy.NoDataAllowed.selector); - - s_tokenProxy.ccipSend(DEST_CHAIN_SELECTOR, message); - } - - function test_CcipSendGasShouldBeZero_Revert() public { - Client.EVMTokenAmount[] memory tokens = new Client.EVMTokenAmount[](1); - tokens[0] = Client.EVMTokenAmount({token: address(s_transferToken), amount: 1e18}); - - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.tokenAmounts = tokens; - message.extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 1})); - - vm.expectRevert(TokenProxy.GasShouldBeZero.selector); - - s_tokenProxy.ccipSend(DEST_CHAIN_SELECTOR, message); - } -} diff --git a/contracts/src/v0.8/ccip/test/arm/ARMProxy.t.sol b/contracts/src/v0.8/ccip/test/arm/ARMProxy.t.sol deleted file mode 100644 index f1889fae755..00000000000 --- a/contracts/src/v0.8/ccip/test/arm/ARMProxy.t.sol +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {IRMN} from "../../interfaces/IRMN.sol"; - -import {ARMProxy} from "../../ARMProxy.sol"; -import {MockRMN} from "../mocks/MockRMN.sol"; -import {RMNSetup} from "./RMNSetup.t.sol"; - -contract ARMProxyTest is RMNSetup { - MockRMN internal s_mockRMN; - ARMProxy internal s_armProxy; - - function setUp() public virtual override { - RMNSetup.setUp(); - s_mockRMN = new MockRMN(); - s_armProxy = new ARMProxy(address(s_rmn)); - } - - function test_ARMIsCursed_Success() public { - s_armProxy.setARM(address(s_mockRMN)); - assertFalse(IRMN(address(s_armProxy)).isCursed()); - s_mockRMN.setGlobalCursed(true); - assertTrue(IRMN(address(s_armProxy)).isCursed()); - } - - function test_ARMIsBlessed_Success() public { - s_armProxy.setARM(address(s_mockRMN)); - s_mockRMN.setTaggedRootBlessed(IRMN.TaggedRoot({commitStore: address(0), root: bytes32(0)}), true); - assertTrue(IRMN(address(s_armProxy)).isBlessed(IRMN.TaggedRoot({commitStore: address(0), root: bytes32(0)}))); - s_mockRMN.setTaggedRootBlessed(IRMN.TaggedRoot({commitStore: address(0), root: bytes32(0)}), false); - assertFalse(IRMN(address(s_armProxy)).isBlessed(IRMN.TaggedRoot({commitStore: address(0), root: bytes32(0)}))); - } - - function test_ARMCallRevertReasonForwarded() public { - bytes memory err = bytes("revert"); - s_mockRMN.setIsCursedRevert(err); - s_armProxy.setARM(address(s_mockRMN)); - vm.expectRevert(abi.encodeWithSelector(MockRMN.CustomError.selector, err)); - IRMN(address(s_armProxy)).isCursed(); - } -} diff --git a/contracts/src/v0.8/ccip/test/arm/RMN.t.sol b/contracts/src/v0.8/ccip/test/arm/RMN.t.sol deleted file mode 100644 index 85501170e35..00000000000 --- a/contracts/src/v0.8/ccip/test/arm/RMN.t.sol +++ /dev/null @@ -1,1068 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {IRMN} from "../../interfaces/IRMN.sol"; - -import {GLOBAL_CURSE_SUBJECT, LIFT_CURSE_VOTE_ADDR, OWNER_CURSE_VOTE_ADDR, RMN} from "../../RMN.sol"; -import {RMNSetup, makeCursesHash, makeSubjects} from "./RMNSetup.t.sol"; - -import {Test} from "forge-std/Test.sol"; - -bytes28 constant GARBAGE_CURSES_HASH = bytes28(keccak256("GARBAGE_CURSES_HASH")); - -contract ConfigCompare is Test { - function assertConfigEq(RMN.Config memory actualConfig, RMN.Config memory expectedConfig) public pure { - assertEq(actualConfig.voters.length, expectedConfig.voters.length); - for (uint256 i = 0; i < expectedConfig.voters.length; ++i) { - RMN.Voter memory expectedVoter = expectedConfig.voters[i]; - RMN.Voter memory actualVoter = actualConfig.voters[i]; - assertEq(actualVoter.blessVoteAddr, expectedVoter.blessVoteAddr); - assertEq(actualVoter.curseVoteAddr, expectedVoter.curseVoteAddr); - assertEq(actualVoter.blessWeight, expectedVoter.blessWeight); - assertEq(actualVoter.curseWeight, expectedVoter.curseWeight); - } - assertEq(actualConfig.blessWeightThreshold, expectedConfig.blessWeightThreshold); - assertEq(actualConfig.curseWeightThreshold, expectedConfig.curseWeightThreshold); - } -} - -contract RMN_constructor is ConfigCompare, RMNSetup { - function test_Constructor_Success() public view { - RMN.Config memory expectedConfig = rmnConstructorArgs(); - (uint32 actualVersion,, RMN.Config memory actualConfig) = s_rmn.getConfigDetails(); - assertEq(actualVersion, 1); - assertConfigEq(actualConfig, expectedConfig); - } -} - -contract RMN_voteToBless is RMNSetup { - function _getFirstBlessVoterAndWeight() internal pure returns (address, uint8) { - RMN.Config memory cfg = rmnConstructorArgs(); - return (cfg.voters[0].blessVoteAddr, cfg.voters[0].blessWeight); - } - - // Success - - function test_RootSuccess() public { - uint256 numRoots = 10; - - (address voter, uint8 voterWeight) = _getFirstBlessVoterAndWeight(); - - for (uint256 i = 1; i <= numRoots; ++i) { - vm.expectEmit(); - emit RMN.VotedToBless(1, voter, makeTaggedRoot(i), voterWeight); - } - - vm.prank(voter); - s_rmn.voteToBless(makeTaggedRootsInclusive(1, numRoots)); - - for (uint256 i = 1; i <= numRoots; ++i) { - assertFalse(s_rmn.isBlessed(makeTaggedRoot(i))); - assertEq(voterWeight, getWeightOfVotesToBlessRoot(makeTaggedRoot(i))); - assertTrue(hasVotedToBlessRoot(voter, makeTaggedRoot(1))); - } - } - - // Reverts - - function test_SenderAlreadyVoted_Revert() public { - (address voter,) = _getFirstBlessVoterAndWeight(); - - vm.startPrank(voter); - s_rmn.voteToBless(makeTaggedRootSingleton(1)); - assertTrue(hasVotedToBlessRoot(voter, makeTaggedRoot(1))); - - uint256 votesToBlessBefore = getWeightOfVotesToBlessRoot(makeTaggedRoot(1)); - vm.expectRevert(RMN.VoteToBlessNoop.selector); - s_rmn.voteToBless(makeTaggedRootSingleton(1)); - assertEq(votesToBlessBefore, getWeightOfVotesToBlessRoot(makeTaggedRoot(1))); - } - - function test_IsAlreadyBlessed_Revert() public { - RMN.Config memory cfg = rmnConstructorArgs(); - - // Bless voters 2,3,4 vote to bless - for (uint256 i = 1; i < cfg.voters.length; i++) { - vm.startPrank(cfg.voters[i].blessVoteAddr); - s_rmn.voteToBless(makeTaggedRootSingleton(1)); - } - - uint256 votesToBlessBefore = getWeightOfVotesToBlessRoot(makeTaggedRoot(1)); - vm.startPrank(cfg.voters[0].blessVoteAddr); - vm.expectRevert(RMN.VoteToBlessNoop.selector); - s_rmn.voteToBless(makeTaggedRootSingleton(1)); - assertEq(votesToBlessBefore, getWeightOfVotesToBlessRoot(makeTaggedRoot(1))); - } - - function test_Curse_Revert() public { - RMN.Config memory cfg = rmnConstructorArgs(); - - for (uint256 i = 0; i < cfg.voters.length; i++) { - vm.startPrank(cfg.voters[i].curseVoteAddr); - s_rmn.voteToCurse(makeCurseId(i), makeSubjects(GLOBAL_CURSE_SUBJECT)); - } - - vm.startPrank(cfg.voters[0].blessVoteAddr); - vm.expectRevert(RMN.VoteToBlessForbiddenDuringActiveGlobalCurse.selector); - s_rmn.voteToBless(makeTaggedRootSingleton(12903)); - } - - function test_UnauthorizedVoter_Revert() public { - vm.startPrank(STRANGER); - vm.expectRevert(abi.encodeWithSelector(RMN.UnauthorizedVoter.selector, STRANGER)); - s_rmn.voteToBless(makeTaggedRootSingleton(12321)); - } -} - -contract RMN_ownerUnbless is RMNSetup { - function test_Unbless_Success() public { - RMN.Config memory cfg = rmnConstructorArgs(); - for (uint256 i = 0; i < cfg.voters.length; ++i) { - vm.startPrank(cfg.voters[i].blessVoteAddr); - s_rmn.voteToBless(makeTaggedRootSingleton(1)); - } - assertTrue(s_rmn.isBlessed(makeTaggedRoot(1))); - - vm.startPrank(OWNER); - s_rmn.ownerResetBlessVotes(makeTaggedRootSingleton(1)); - assertFalse(s_rmn.isBlessed(makeTaggedRoot(1))); - } -} - -contract RMN_unvoteToCurse is RMNSetup { - uint256 internal s_curser; - bytes28 internal s_cursesHash; - - function setUp() public override { - RMNSetup.setUp(); - RMN.Config memory cfg = rmnConstructorArgs(); - - s_curser = 0; - vm.startPrank(cfg.voters[s_curser].curseVoteAddr); - s_rmn.voteToCurse(makeCurseId(1), makeSubjects(0)); - bytes28 expectedCursesHash = makeCursesHash(makeCurseId(1)); - assertFalse(s_rmn.isCursed()); - (address[] memory cursers, bytes28[] memory cursesHashes, uint16 weight, bool cursed) = s_rmn.getCurseProgress(0); - assertEq(1, cursers.length); - assertEq(cfg.voters[s_curser].curseVoteAddr, cursers[0]); - assertEq(cfg.voters[s_curser].curseWeight, weight); - assertEq(1, cursesHashes.length); - assertEq(expectedCursesHash, cursesHashes[0]); - assertFalse(cursed); - - s_cursesHash = expectedCursesHash; - } - - function test_UnauthorizedVoter() public { - RMN.Config memory cfg = rmnConstructorArgs(); - // Someone else cannot unvote to curse on the curser's behalf. - address[] memory unauthorized = new address[](3); - unauthorized[0] = cfg.voters[s_curser].blessVoteAddr; - unauthorized[1] = cfg.voters[s_curser ^ 1].blessVoteAddr; - unauthorized[2] = OWNER; - - for (uint256 i = 0; i < unauthorized.length; ++i) { - bytes memory expectedRevert = abi.encodeWithSelector(RMN.UnauthorizedVoter.selector, unauthorized[i]); - vm.startPrank(unauthorized[i]); - { - // should fail when using the correct curses hash - RMN.UnvoteToCurseRequest[] memory reqs = new RMN.UnvoteToCurseRequest[](1); - reqs[0] = RMN.UnvoteToCurseRequest({subject: 0, cursesHash: s_cursesHash}); - vm.expectRevert(expectedRevert); - s_rmn.unvoteToCurse(reqs); - } - { - // should fail when using garbage curses hash - RMN.UnvoteToCurseRequest[] memory reqs = new RMN.UnvoteToCurseRequest[](1); - reqs[0] = RMN.UnvoteToCurseRequest({subject: 0, cursesHash: GARBAGE_CURSES_HASH}); - vm.expectRevert(expectedRevert); - s_rmn.unvoteToCurse(reqs); - } - } - } - - function test_InvalidCursesHash() public { - RMN.Config memory cfg = rmnConstructorArgs(); - vm.startPrank(cfg.voters[s_curser].curseVoteAddr); - RMN.UnvoteToCurseRequest[] memory reqs = new RMN.UnvoteToCurseRequest[](1); - reqs[0] = RMN.UnvoteToCurseRequest({subject: 0, cursesHash: GARBAGE_CURSES_HASH}); - vm.expectRevert(RMN.UnvoteToCurseNoop.selector); - s_rmn.unvoteToCurse(reqs); - } - - function test_ValidCursesHash() public { - RMN.Config memory cfg = rmnConstructorArgs(); - vm.startPrank(cfg.voters[s_curser].curseVoteAddr); - RMN.UnvoteToCurseRequest[] memory reqs = new RMN.UnvoteToCurseRequest[](1); - reqs[0] = RMN.UnvoteToCurseRequest({subject: 0, cursesHash: s_cursesHash}); - s_rmn.unvoteToCurse(reqs); // succeeds - } - - function test_OwnerSucceeds() public { - RMN.Config memory cfg = rmnConstructorArgs(); - vm.startPrank(OWNER); - RMN.OwnerUnvoteToCurseRequest[] memory reqs = new RMN.OwnerUnvoteToCurseRequest[](1); - reqs[0] = RMN.OwnerUnvoteToCurseRequest({ - curseVoteAddr: cfg.voters[s_curser].curseVoteAddr, - unit: RMN.UnvoteToCurseRequest({subject: 0, cursesHash: s_cursesHash}), - forceUnvote: false - }); - s_rmn.ownerUnvoteToCurse(reqs); - } - - function test_OwnerSkips() public { - RMN.Config memory cfg = rmnConstructorArgs(); - vm.startPrank(OWNER); - RMN.OwnerUnvoteToCurseRequest[] memory reqs = new RMN.OwnerUnvoteToCurseRequest[](1); - reqs[0] = RMN.OwnerUnvoteToCurseRequest({ - curseVoteAddr: cfg.voters[s_curser].curseVoteAddr, - unit: RMN.UnvoteToCurseRequest({subject: 0, cursesHash: GARBAGE_CURSES_HASH}), - forceUnvote: false - }); - - vm.expectEmit(); - emit RMN.SkippedUnvoteToCurse(cfg.voters[s_curser].curseVoteAddr, 0, s_cursesHash, GARBAGE_CURSES_HASH); - vm.expectRevert(RMN.UnvoteToCurseNoop.selector); - s_rmn.ownerUnvoteToCurse(reqs); - } - - function test_VotersCantLiftCurseButOwnerCan() public { - vm.stopPrank(); - RMN.Config memory cfg = rmnConstructorArgs(); - // s_curser has voted to curse during setUp - { - (address[] memory voters, bytes28[] memory cursesHashes, uint16 accWeight, bool cursed) = - s_rmn.getCurseProgress(0); - assertEq(accWeight, cfg.voters[s_curser].curseWeight); - assertFalse(cursed); - assertEq(voters.length, 1); - assertEq(cursesHashes.length, 1); - assertEq(voters[0], cfg.voters[s_curser].curseVoteAddr); - assertEq(cursesHashes[0], makeCursesHash(makeCurseId(1))); - } - // everyone else votes now, same curse id, same subject - { - for (uint256 i = 0; i < cfg.voters.length; ++i) { - if (i == s_curser) continue; // already voted to curse - vm.prank(cfg.voters[i].curseVoteAddr); - s_rmn.voteToCurse(makeCurseId(1), makeSubjects(0)); - } - } - // subject must be cursed now - { - assertTrue(s_rmn.isCursed(0)); - } - // curse progress should be as full as it can get - { - (address[] memory voters, bytes28[] memory cursesHashes, uint16 accWeight, bool cursed) = - s_rmn.getCurseProgress(0); - uint256 allWeights; - for (uint256 i = 0; i < cfg.voters.length; i++) { - allWeights += cfg.voters[i].curseWeight; - } - assertEq(accWeight, allWeights); - assertTrue(cursed); - assertEq(voters.length, cfg.voters.length); - assertEq(cursesHashes.length, cfg.voters.length); - for (uint256 i = 0; i < cfg.voters.length; ++i) { - assertEq(voters[i], cfg.voters[i].curseVoteAddr); - assertEq(cursesHashes[i], makeCursesHash(makeCurseId(1))); - } - } - // everyone unvotes to curse, successfully - { - for (uint256 i = 0; i < cfg.voters.length; ++i) { - vm.prank(cfg.voters[i].curseVoteAddr); - RMN.UnvoteToCurseRequest[] memory reqs = new RMN.UnvoteToCurseRequest[](1); - reqs[0] = RMN.UnvoteToCurseRequest({subject: 0, cursesHash: makeCursesHash(makeCurseId(1))}); - s_rmn.unvoteToCurse(reqs); - } - } - // curse should still be in place as only the owner can lift it - { - assertTrue(s_rmn.isCursed(0)); - } - // curse progress should be empty, expect for the cursed flag - { - (address[] memory voters, bytes28[] memory cursesHashes, uint16 accWeight, bool cursed) = - s_rmn.getCurseProgress(0); - assertEq(accWeight, 0); - assertTrue(cursed); - assertEq(voters.length, 0); - assertEq(cursesHashes.length, 0); - } - // owner lifts curse - { - RMN.OwnerUnvoteToCurseRequest[] memory ownerReq = new RMN.OwnerUnvoteToCurseRequest[](1); - ownerReq[0] = RMN.OwnerUnvoteToCurseRequest({ - curseVoteAddr: LIFT_CURSE_VOTE_ADDR, - unit: RMN.UnvoteToCurseRequest({subject: 0, cursesHash: 0}), - forceUnvote: false - }); - vm.prank(OWNER); - s_rmn.ownerUnvoteToCurse(ownerReq); - } - // curse should now be lifted - { - assertFalse(s_rmn.isCursed(0)); - } - } -} - -contract RMN_voteToCurse_2 is RMNSetup { - function initialConfig() internal pure returns (RMN.Config memory) { - RMN.Config memory cfg = RMN.Config({voters: new RMN.Voter[](3), blessWeightThreshold: 1, curseWeightThreshold: 3}); - cfg.voters[0] = - RMN.Voter({blessVoteAddr: BLESS_VOTER_1, curseVoteAddr: CURSE_VOTER_1, blessWeight: 1, curseWeight: 1}); - cfg.voters[1] = - RMN.Voter({blessVoteAddr: BLESS_VOTER_2, curseVoteAddr: CURSE_VOTER_2, blessWeight: 1, curseWeight: 1}); - cfg.voters[2] = - RMN.Voter({blessVoteAddr: BLESS_VOTER_3, curseVoteAddr: CURSE_VOTER_3, blessWeight: 1, curseWeight: 1}); - return cfg; - } - - function setUp() public override { - vm.prank(OWNER); - s_rmn = new RMN(initialConfig()); - } - - function test_VotesAreDroppedIfSubjectIsNotCursedDuringConfigChange() public { - // vote to curse the subject from an insufficient number of voters, one voter - { - RMN.Config memory cfg = initialConfig(); - vm.prank(cfg.voters[0].curseVoteAddr); - s_rmn.voteToCurse(makeCurseId(1), makeSubjects(0)); - } - // vote must be in place - { - (address[] memory voters, bytes28[] memory cursesHashes, uint16 accWeight, bool cursed) = - s_rmn.getCurseProgress(0); - assertEq(voters.length, 1); - assertEq(cursesHashes.length, 1); - assertEq(accWeight, 1); - assertFalse(cursed); - } - // change config to include only the first voter, i.e., initialConfig().voters[0] - { - RMN.Config memory cfg = initialConfig(); - RMN.Voter[] memory voters = cfg.voters; - assembly { - mstore(voters, 1) - } - cfg.curseWeightThreshold = 1; - vm.prank(OWNER); - s_rmn.setConfig(cfg); - } - // vote must be dropped - { - (address[] memory voters, bytes28[] memory cursesHashes, uint16 accWeight, bool cursed) = - s_rmn.getCurseProgress(0); - assertEq(voters.length, 0); - assertEq(cursesHashes.length, 0); - assertEq(accWeight, 0); - assertFalse(cursed); - } - // cause an owner curse now - { - vm.prank(OWNER); - s_rmn.ownerCurse(makeCurseId(1), makeSubjects(0)); - } - // only the owner curse must be visible - { - (address[] memory voters, bytes28[] memory cursesHashes, uint16 accWeight, bool cursed) = - s_rmn.getCurseProgress(0); - assertEq(voters.length, 1); - assertEq(voters[0], OWNER_CURSE_VOTE_ADDR); - assertEq(cursesHashes.length, 1); - assertEq(cursesHashes[0], makeCursesHash(makeCurseId(1))); - assertEq(accWeight, 0); - assertTrue(cursed); - } - } - - function test_VotesAreRetainedIfSubjectIsCursedDuringConfigChange() public { - uint256 numVotersInitially = initialConfig().voters.length; - // curse the subject with votes from all voters - { - RMN.Config memory cfg = initialConfig(); - for (uint256 i = 0; i < cfg.voters.length; ++i) { - vm.prank(cfg.voters[i].curseVoteAddr); - s_rmn.voteToCurse(makeCurseId(1), makeSubjects(0)); - } - } - // subject is now cursed - { - assertTrue(s_rmn.isCursed(0)); - } - // throw in an owner curse - { - vm.prank(OWNER); - s_rmn.ownerCurse(makeCurseId(1), makeSubjects(0)); - } - - uint256 snapshot = vm.snapshot(); - - for (uint256 keepVoters = 1; keepVoters <= numVotersInitially; ++keepVoters) { - vm.revertTo(snapshot); - - // change config to include only the first #keepVoters voters, i.e., initialConfig().voters[0..keepVoters] - { - RMN.Config memory cfg = initialConfig(); - RMN.Voter[] memory voters = cfg.voters; - assembly { - mstore(voters, keepVoters) - } - cfg.curseWeightThreshold = uint16(keepVoters); - vm.prank(OWNER); - s_rmn.setConfig(cfg); - } - // subject is still cursed - { - assertTrue(s_rmn.isCursed(0)); - } - // all votes from the first keepVoters & owner must be present - { - (address[] memory voters, bytes28[] memory cursesHashes, uint16 accWeight, bool cursed) = - s_rmn.getCurseProgress(0); - assertEq(voters.length, keepVoters + 1 /* owner */ ); - assertEq(cursesHashes.length, keepVoters + 1 /* owner */ ); - assertEq(accWeight, keepVoters /* 1 per voter */ ); - assertTrue(cursed); - for (uint256 i = 0; i < keepVoters; ++i) { - assertEq(voters[i], initialConfig().voters[i].curseVoteAddr); - assertEq(cursesHashes[i], makeCursesHash(makeCurseId(1))); - } - assertEq(voters[voters.length - 1], OWNER_CURSE_VOTE_ADDR); - assertEq(cursesHashes[cursesHashes.length - 1], makeCursesHash(makeCurseId(1))); - } - // the owner unvoting for all is not enough to lift the curse, because remember that the owner has an active vote - // also - { - for (uint256 i = 0; i < keepVoters; ++i) { - RMN.OwnerUnvoteToCurseRequest[] memory ownerReq = new RMN.OwnerUnvoteToCurseRequest[](1); - ownerReq[0] = RMN.OwnerUnvoteToCurseRequest({ - curseVoteAddr: initialConfig().voters[i].curseVoteAddr, - unit: RMN.UnvoteToCurseRequest({subject: 0, cursesHash: makeCursesHash(makeCurseId(1))}), - forceUnvote: false - }); - vm.prank(OWNER); - s_rmn.ownerUnvoteToCurse(ownerReq); - - assertTrue(s_rmn.isCursed(0)); - } - } - // after owner unvotes for themselves, finally, the curse will be lifted - { - RMN.OwnerUnvoteToCurseRequest[] memory ownerReq = new RMN.OwnerUnvoteToCurseRequest[](1); - ownerReq[0] = RMN.OwnerUnvoteToCurseRequest({ - curseVoteAddr: OWNER_CURSE_VOTE_ADDR, - unit: RMN.UnvoteToCurseRequest({subject: 0, cursesHash: makeCursesHash(makeCurseId(1))}), - forceUnvote: false - }); - vm.prank(OWNER); - s_rmn.ownerUnvoteToCurse(ownerReq); - - assertFalse(s_rmn.isCursed(0)); - } - } - } -} - -contract RMN_voteToCurse is RMNSetup { - function _getFirstCurseVoterAndWeight() internal pure returns (address, uint8) { - RMN.Config memory cfg = rmnConstructorArgs(); - return (cfg.voters[0].curseVoteAddr, cfg.voters[0].curseWeight); - } - - // Success - - function test_CurseOnlyWhenThresholdReached_Success() public { - uint256 numSubjects = 3; - uint256 maxNumRevotes = 2; - - RMN.Config memory cfg = rmnConstructorArgs(); - bytes16[] memory subjects = new bytes16[](numSubjects); - for (uint256 i = 0; i < numSubjects; ++i) { - subjects[i] = bytes16(uint128(i)); - } - for (uint256 numRevotes = 1; numRevotes <= maxNumRevotes; ++numRevotes) { - // all voters but the last vote, but can't surpass the curse weight threshold - for (uint256 i = 0; i < cfg.voters.length - 1; ++i) { - vm.prank(cfg.voters[i].curseVoteAddr); - s_rmn.voteToCurse(makeCurseId(numRevotes), subjects); - } - // no curse is yet active, last voter also needs to vote for any curse to be active - { - // ensure every subject is not cursed - for (uint256 i = 0; i < numSubjects; ++i) { - assertFalse(s_rmn.isCursed(subjects[i])); - } - // ensure every vote has been recorded - assertEq( - s_rmn.getRecordedCurseRelatedOpsCount(), - 1 /* setConfig */ + (cfg.voters.length - 1) * numRevotes * numSubjects - ); - } - } - - // last voter now votes - vm.prank(cfg.voters[cfg.voters.length - 1].curseVoteAddr); - s_rmn.voteToCurse(makeCurseId(0), subjects); - // curses should be now active - { - // ensure every subject is now cursed - for (uint256 i = 0; i < numSubjects; ++i) { - assertTrue(s_rmn.isCursed(subjects[i])); - } - // ensure every vote has been recorded - assertEq( - s_rmn.getRecordedCurseRelatedOpsCount(), - 1 /* setConfig */ + ((cfg.voters.length - 1) * maxNumRevotes + 1) * numSubjects - ); - } - } - - function test_VoteToCurse_NoCurse_Success() public { - (address voter, uint8 weight) = _getFirstCurseVoterAndWeight(); - vm.startPrank(voter); - vm.expectEmit(); - emit RMN.VotedToCurse( - 1, // configVersion - voter, - GLOBAL_CURSE_SUBJECT, - makeCurseId(123), - weight, - 1234567890, // blockTimestamp - makeCursesHash(makeCurseId(123)), // cursesHash - weight - ); - - s_rmn.voteToCurse(makeCurseId(123), makeSubjects(GLOBAL_CURSE_SUBJECT)); - - (address[] memory voters,, uint16 votes, bool cursed) = s_rmn.getCurseProgress(GLOBAL_CURSE_SUBJECT); - assertEq(1, voters.length); - assertEq(voter, voters[0]); - assertEq(weight, votes); - assertFalse(cursed); - } - - function test_VoteToCurse_YesCurse_Success() public { - RMN.Config memory cfg = rmnConstructorArgs(); - for (uint256 i = 0; i < cfg.voters.length - 1; ++i) { - vm.startPrank(cfg.voters[i].curseVoteAddr); - s_rmn.voteToCurse(makeCurseId(1), makeSubjects(0)); - } - - vm.expectEmit(); - emit RMN.Cursed(1, 0, uint64(block.timestamp)); - - vm.startPrank(cfg.voters[cfg.voters.length - 1].curseVoteAddr); - vm.resumeGasMetering(); - s_rmn.voteToCurse(makeCurseId(1), makeSubjects(0)); - } - - function test_EvenIfAlreadyCursed_Success() public { - RMN.Config memory cfg = rmnConstructorArgs(); - uint16 weightSum = 0; - for (uint256 i = 0; i < cfg.voters.length; ++i) { - vm.startPrank(cfg.voters[i].curseVoteAddr); - s_rmn.voteToCurse(makeCurseId(i), makeSubjects(0)); - weightSum += cfg.voters[i].curseWeight; - } - - // Not part of the assertion of this test but good to have as a sanity - // check. We want a curse to be active in order for the ultimate assertion - // to make sense. - assert(s_rmn.isCursed(0)); - - vm.expectEmit(); - emit RMN.VotedToCurse( - 1, // configVersion - cfg.voters[cfg.voters.length - 1].curseVoteAddr, - 0, // subject - makeCurseId(cfg.voters.length + 1), // this curse id - cfg.voters[cfg.voters.length - 1].curseWeight, - uint64(block.timestamp), // blockTimestamp - makeCursesHash(makeCurseId(cfg.voters.length - 1), makeCurseId(cfg.voters.length + 1)), // cursesHash - weightSum // accumulatedWeight - ); - // Asserts that this call to vote with a new curse id goes through with no - // reverts even when the RMN contract is cursed. - s_rmn.voteToCurse(makeCurseId(cfg.voters.length + 1), makeSubjects(0)); - } - - function test_OwnerCanCurseAndUncurse() public { - vm.startPrank(OWNER); - bytes28 expectedCursesHash = makeCursesHash(makeCurseId(0)); - vm.expectEmit(); - emit RMN.VotedToCurse( - 1, // configVersion - OWNER_CURSE_VOTE_ADDR, // owner - 0, // subject - makeCurseId(0), // curse id - 0, // weight - uint64(block.timestamp), // blockTimestamp - expectedCursesHash, // cursesHash - 0 // accumulatedWeight - ); - vm.expectEmit(); - emit RMN.Cursed( - 1, // configVersion - 0, // subject - uint64(block.timestamp) // blockTimestamp - ); - s_rmn.ownerCurse(makeCurseId(0), makeSubjects(0)); - - { - (address[] memory voters, bytes28[] memory cursesHashes, uint24 accWeight, bool cursed) = - s_rmn.getCurseProgress(0); - assertEq(voters.length, 1); - assertEq(voters[0], OWNER_CURSE_VOTE_ADDR /* owner */ ); - assertEq(cursesHashes.length, 1); - assertEq(cursesHashes[0], expectedCursesHash); - assertEq(accWeight, 0); - assertTrue(cursed); - } - - // ownerCurse again, should cause a vote to appear and a change in curses hash - expectedCursesHash = makeCursesHash(makeCurseId(0), makeCurseId(1)); - vm.expectEmit(); - emit RMN.VotedToCurse( - 1, // configVersion - OWNER_CURSE_VOTE_ADDR, // owner - 0, // subject - makeCurseId(1), // curse id - 0, // weight - uint64(block.timestamp), // blockTimestamp - expectedCursesHash, // cursesHash - 0 // accumulatedWeight - ); - s_rmn.ownerCurse(makeCurseId(1), makeSubjects(0)); - - { - (address[] memory voters, bytes28[] memory cursesHashes, uint24 accWeight, bool cursed) = - s_rmn.getCurseProgress(0); - assertEq(voters.length, 1); - assertEq(voters[0], OWNER_CURSE_VOTE_ADDR /* owner */ ); - assertEq(cursesHashes.length, 1); - assertEq(cursesHashes[0], expectedCursesHash); - assertEq(accWeight, 0); - assertTrue(cursed); - } - - RMN.OwnerUnvoteToCurseRequest[] memory unvoteReqs = new RMN.OwnerUnvoteToCurseRequest[](1); - unvoteReqs[0] = RMN.OwnerUnvoteToCurseRequest({ - curseVoteAddr: OWNER_CURSE_VOTE_ADDR, - unit: RMN.UnvoteToCurseRequest({subject: 0, cursesHash: 0}), - forceUnvote: true // TODO: test with forceUnvote false also - }); - vm.expectEmit(); - emit RMN.CurseLifted(0); - s_rmn.ownerUnvoteToCurse(unvoteReqs); - { - (address[] memory voters, bytes28[] memory cursesHashes, uint24 accWeight, bool cursed) = - s_rmn.getCurseProgress(0); - assertEq(voters.length, 0); - assertEq(cursesHashes.length, 0); - assertEq(accWeight, 0); - assertFalse(cursed); - } - } - - // Reverts - - function test_UnauthorizedVoter_Revert() public { - vm.startPrank(STRANGER); - - vm.expectRevert(abi.encodeWithSelector(RMN.UnauthorizedVoter.selector, STRANGER)); - s_rmn.voteToCurse(makeCurseId(12312), makeSubjects(0)); - } - - function test_ReusedCurseId_Revert() public { - (address voter,) = _getFirstCurseVoterAndWeight(); - vm.startPrank(voter); - s_rmn.voteToCurse(makeCurseId(1), makeSubjects(0)); - - vm.expectRevert(abi.encodeWithSelector(RMN.ReusedCurseId.selector, voter, makeCurseId(1))); - s_rmn.voteToCurse(makeCurseId(1), makeSubjects(0)); - } - - function test_RepeatedSubject_Revert() public { - (address voter,) = _getFirstCurseVoterAndWeight(); - vm.prank(voter); - - bytes16 subject = bytes16(uint128(1)); - - vm.expectRevert(RMN.SubjectsMustBeStrictlyIncreasing.selector); - s_rmn.voteToCurse(makeCurseId(1), makeSubjects(subject, subject)); - } - - function test_EmptySubjects_Revert() public { - (address voter,) = _getFirstCurseVoterAndWeight(); - vm.prank(voter); - - vm.expectRevert(RMN.VoteToCurseNoop.selector); - s_rmn.voteToCurse(makeCurseId(1), new bytes16[](0)); - } -} - -contract RMN_ownerUnvoteToCurse is RMNSetup { - // These cursers are going to curse in setUp curseCount times. - function getCursersAndCurseCounts() internal pure returns (address[] memory cursers, uint32[] memory curseCounts) { - // NOTE: Change this when changing setUp or rmnConstructorArgs. - // This is a bit ugly and error prone but if we read from storage we would - // not get an accurate gas reading for ownerUnvoteToCurse when we need it. - cursers = new address[](4); - cursers[0] = CURSE_VOTER_1; - cursers[1] = CURSE_VOTER_2; - cursers[2] = CURSE_VOTER_3; - cursers[3] = CURSE_VOTER_4; - curseCounts = new uint32[](cursers.length); - for (uint256 i = 0; i < cursers.length; ++i) { - curseCounts[i] = 1; - } - } - - function setUp() public virtual override { - RMNSetup.setUp(); - (address[] memory cursers, uint32[] memory curseCounts) = getCursersAndCurseCounts(); - for (uint256 i = 0; i < cursers.length; ++i) { - vm.startPrank(cursers[i]); - for (uint256 j = 0; j < curseCounts[i]; ++j) { - s_rmn.voteToCurse(makeCurseId(j), makeSubjects(GLOBAL_CURSE_SUBJECT)); - } - } - } - - function ownerUnvoteToCurse() internal { - s_rmn.ownerUnvoteToCurse(makeOwnerUnvoteToCurseRequests()); - } - - function makeOwnerUnvoteToCurseRequests() internal pure returns (RMN.OwnerUnvoteToCurseRequest[] memory) { - (address[] memory cursers,) = getCursersAndCurseCounts(); - RMN.OwnerUnvoteToCurseRequest[] memory reqs = new RMN.OwnerUnvoteToCurseRequest[](cursers.length); - for (uint256 i = 0; i < cursers.length; ++i) { - reqs[i] = RMN.OwnerUnvoteToCurseRequest({ - curseVoteAddr: cursers[i], - unit: RMN.UnvoteToCurseRequest({subject: GLOBAL_CURSE_SUBJECT, cursesHash: bytes28(0)}), - forceUnvote: true - }); - } - return reqs; - } - - // Success - - function test_OwnerUnvoteToCurseSuccess_gas() public { - vm.pauseGasMetering(); - vm.startPrank(OWNER); - - vm.expectEmit(); - emit RMN.CurseLifted(GLOBAL_CURSE_SUBJECT); - - vm.resumeGasMetering(); - ownerUnvoteToCurse(); - vm.pauseGasMetering(); - - assertFalse(s_rmn.isCursed()); - (address[] memory voters, bytes28[] memory cursesHashes, uint256 weight, bool cursed) = - s_rmn.getCurseProgress(GLOBAL_CURSE_SUBJECT); - assertEq(voters.length, 0); - assertEq(cursesHashes.length, 0); - assertEq(weight, 0); - assertFalse(cursed); - vm.resumeGasMetering(); - } - - function test_IsIdempotent() public { - vm.startPrank(OWNER); - ownerUnvoteToCurse(); - vm.expectRevert(RMN.UnvoteToCurseNoop.selector); - ownerUnvoteToCurse(); - - assertFalse(s_rmn.isCursed()); - (address[] memory voters, bytes28[] memory cursesHashes, uint256 weight, bool cursed) = - s_rmn.getCurseProgress(GLOBAL_CURSE_SUBJECT); - assertEq(voters.length, 0); - assertEq(cursesHashes.length, 0); - assertEq(weight, 0); - assertFalse(cursed); - } - - function test_CanBlessAndCurseAfterGlobalCurseIsLifted() public { - // Contract is already cursed due to setUp. - - // Owner unvotes to curse. - vm.startPrank(OWNER); - vm.expectEmit(); - emit RMN.CurseLifted(GLOBAL_CURSE_SUBJECT); - ownerUnvoteToCurse(); - - // Contract is now uncursed. - assertFalse(s_rmn.isCursed()); - - // Vote to bless should go through. - vm.startPrank(BLESS_VOTER_1); - s_rmn.voteToBless(makeTaggedRootSingleton(2387489729)); - - // Vote to curse should go through. - vm.startPrank(CURSE_VOTER_1); - s_rmn.voteToCurse(makeCurseId(73894728973), makeSubjects(GLOBAL_CURSE_SUBJECT)); - } - - // Reverts - - function test_NonOwner_Revert() public { - vm.startPrank(STRANGER); - vm.expectRevert("Only callable by owner"); - ownerUnvoteToCurse(); - } - - function test_UnknownVoter_Revert() public { - vm.stopPrank(); - RMN.OwnerUnvoteToCurseRequest[] memory reqs = new RMN.OwnerUnvoteToCurseRequest[](1); - reqs[0] = RMN.OwnerUnvoteToCurseRequest({ - curseVoteAddr: STRANGER, - unit: RMN.UnvoteToCurseRequest({subject: GLOBAL_CURSE_SUBJECT, cursesHash: bytes28(0)}), - forceUnvote: true - }); - - vm.prank(OWNER); - vm.expectEmit(); - emit RMN.SkippedUnvoteToCurse(STRANGER, GLOBAL_CURSE_SUBJECT, bytes28(0), bytes28(0)); - vm.expectRevert(RMN.UnvoteToCurseNoop.selector); - s_rmn.ownerUnvoteToCurse(reqs); - - // no effect on cursedness - assertTrue(s_rmn.isCursed(GLOBAL_CURSE_SUBJECT)); - } -} - -contract RMN_setConfig is ConfigCompare, RMNSetup { - /// @notice Test-specific function to use only in setConfig tests - function getDifferentConfigArgs() private pure returns (RMN.Config memory) { - RMN.Voter[] memory voters = new RMN.Voter[](2); - voters[0] = RMN.Voter({ - blessVoteAddr: BLESS_VOTER_1, - curseVoteAddr: CURSE_VOTER_1, - blessWeight: WEIGHT_1, - curseWeight: WEIGHT_1 - }); - voters[1] = RMN.Voter({ - blessVoteAddr: BLESS_VOTER_2, - curseVoteAddr: CURSE_VOTER_2, - blessWeight: WEIGHT_10, - curseWeight: WEIGHT_10 - }); - return RMN.Config({ - voters: voters, - blessWeightThreshold: WEIGHT_1 + WEIGHT_10, - curseWeightThreshold: WEIGHT_1 + WEIGHT_10 - }); - } - - function setUp() public virtual override { - RMNSetup.setUp(); - RMN.Config memory cfg = rmnConstructorArgs(); - - // Setup some partial state - vm.startPrank(cfg.voters[0].blessVoteAddr); - s_rmn.voteToBless(makeTaggedRootSingleton(1)); - vm.startPrank(cfg.voters[1].blessVoteAddr); - s_rmn.voteToBless(makeTaggedRootSingleton(1)); - vm.startPrank(cfg.voters[1].curseVoteAddr); - s_rmn.voteToCurse(makeCurseId(1), makeSubjects(0)); - } - - // Success - - event ConfigSet(uint32 indexed configVersion, RMN.Config config); - - function test_VoteToBlessByEjectedVoter_Revert() public { - // Previous config included BLESS_VOTER_4. Change to new config that doesn't. - RMN.Config memory cfg = getDifferentConfigArgs(); - vm.startPrank(OWNER); - s_rmn.setConfig(cfg); - - // BLESS_VOTER_4 is not part of cfg anymore, vote to bless should revert. - vm.startPrank(BLESS_VOTER_4); - vm.expectRevert(abi.encodeWithSelector(RMN.UnauthorizedVoter.selector, BLESS_VOTER_4)); - s_rmn.voteToBless(makeTaggedRootSingleton(2)); - } - - function test_SetConfigSuccess_gas() public { - vm.pauseGasMetering(); - RMN.Config memory cfg = getDifferentConfigArgs(); - - vm.startPrank(OWNER); - vm.expectEmit(); - emit ConfigSet(2, cfg); - - (uint32 configVersionBefore,,) = s_rmn.getConfigDetails(); - vm.resumeGasMetering(); - s_rmn.setConfig(cfg); - vm.pauseGasMetering(); - // Assert VersionedConfig has changed correctly - (uint32 configVersionAfter,, RMN.Config memory configAfter) = s_rmn.getConfigDetails(); - assertEq(configVersionBefore + 1, configVersionAfter); - assertConfigEq(configAfter, cfg); - - // Assert that curse votes have been cleared - - (address[] memory curseVoters, bytes28[] memory cursesHashes, uint256 curseWeight, bool cursed) = - s_rmn.getCurseProgress(0); - assertEq(0, curseVoters.length); - assertEq(0, cursesHashes.length); - assertEq(0, curseWeight); - assertFalse(cursed); - - // Assert that good votes have been cleared - uint256 votesToBlessRoot = getWeightOfVotesToBlessRoot(makeTaggedRoot(1)); - assertEq(ZERO, votesToBlessRoot); - assertFalse(hasVotedToBlessRoot(cfg.voters[0].blessVoteAddr, makeTaggedRoot(1))); - assertFalse(hasVotedToBlessRoot(cfg.voters[1].blessVoteAddr, makeTaggedRoot(1))); - vm.resumeGasMetering(); - } - - // Reverts - - function test_NonOwner_Revert() public { - RMN.Config memory cfg = getDifferentConfigArgs(); - - vm.startPrank(STRANGER); - vm.expectRevert("Only callable by owner"); - s_rmn.setConfig(cfg); - } - - function test_VotersLengthIsZero_Revert() public { - vm.startPrank(OWNER); - vm.expectRevert(RMN.InvalidConfig.selector); - s_rmn.setConfig(RMN.Config({voters: new RMN.Voter[](0), blessWeightThreshold: 1, curseWeightThreshold: 1})); - } - - function test_EitherThresholdIsZero_Revert() public { - RMN.Config memory cfg = getDifferentConfigArgs(); - - vm.startPrank(OWNER); - vm.expectRevert(RMN.InvalidConfig.selector); - s_rmn.setConfig( - RMN.Config({voters: cfg.voters, blessWeightThreshold: ZERO, curseWeightThreshold: cfg.curseWeightThreshold}) - ); - vm.expectRevert(RMN.InvalidConfig.selector); - s_rmn.setConfig( - RMN.Config({voters: cfg.voters, blessWeightThreshold: cfg.blessWeightThreshold, curseWeightThreshold: ZERO}) - ); - } - - function test_BlessVoterIsZeroAddress_Revert() public { - RMN.Config memory cfg = getDifferentConfigArgs(); - - vm.startPrank(OWNER); - cfg.voters[0].blessVoteAddr = ZERO_ADDRESS; - vm.expectRevert(RMN.InvalidConfig.selector); - s_rmn.setConfig(cfg); - } - - function test_WeightIsZeroAddress_Revert() public { - RMN.Config memory cfg = getDifferentConfigArgs(); - - vm.startPrank(OWNER); - cfg.voters[0].blessWeight = ZERO; - cfg.voters[0].curseWeight = ZERO; - vm.expectRevert(RMN.InvalidConfig.selector); - s_rmn.setConfig(cfg); - } - - function test_TotalWeightsSmallerThanEachThreshold_Revert() public { - RMN.Config memory cfg = getDifferentConfigArgs(); - - vm.startPrank(OWNER); - vm.expectRevert(RMN.InvalidConfig.selector); - s_rmn.setConfig( - RMN.Config({voters: cfg.voters, blessWeightThreshold: WEIGHT_40, curseWeightThreshold: cfg.curseWeightThreshold}) - ); - vm.expectRevert(RMN.InvalidConfig.selector); - s_rmn.setConfig( - RMN.Config({voters: cfg.voters, blessWeightThreshold: cfg.blessWeightThreshold, curseWeightThreshold: WEIGHT_40}) - ); - } - - function test_RepeatedAddress_Revert() public { - RMN.Config memory cfg = getDifferentConfigArgs(); - - vm.startPrank(OWNER); - cfg.voters[0].blessVoteAddr = cfg.voters[1].curseVoteAddr; - vm.expectRevert(RMN.InvalidConfig.selector); - s_rmn.setConfig(cfg); - } -} - -contract RMN_permaBlessing is RMNSetup { - function addresses() private pure returns (address[] memory) { - return new address[](0); - } - - function addresses(address a) private pure returns (address[] memory) { - address[] memory arr = new address[](1); - arr[0] = a; - return arr; - } - - function addresses(address a, address b) private pure returns (address[] memory) { - address[] memory arr = new address[](2); - arr[0] = a; - arr[1] = b; - return arr; - } - - function test_PermaBlessing() public { - bytes32 SOME_ROOT = bytes32(~uint256(0)); - address COMMIT_STORE_1 = makeAddr("COMMIT_STORE_1"); - address COMMIT_STORE_2 = makeAddr("COMMIT_STORE_2"); - IRMN.TaggedRoot memory taggedRootCommitStore1 = IRMN.TaggedRoot({root: SOME_ROOT, commitStore: COMMIT_STORE_1}); - IRMN.TaggedRoot memory taggedRootCommitStore2 = IRMN.TaggedRoot({root: SOME_ROOT, commitStore: COMMIT_STORE_2}); - - assertFalse(s_rmn.isBlessed(taggedRootCommitStore1)); - assertFalse(s_rmn.isBlessed(taggedRootCommitStore2)); - assertEq(s_rmn.getPermaBlessedCommitStores(), addresses()); - - // only owner can mutate permaBlessedCommitStores - vm.prank(STRANGER); - vm.expectRevert("Only callable by owner"); - s_rmn.ownerRemoveThenAddPermaBlessedCommitStores(addresses(), addresses(COMMIT_STORE_1)); - - vm.prank(OWNER); - s_rmn.ownerRemoveThenAddPermaBlessedCommitStores(addresses(), addresses(COMMIT_STORE_1)); - assertTrue(s_rmn.isBlessed(taggedRootCommitStore1)); - assertFalse(s_rmn.isBlessed(taggedRootCommitStore2)); - assertEq(s_rmn.getPermaBlessedCommitStores(), addresses(COMMIT_STORE_1)); - - vm.prank(OWNER); - s_rmn.ownerRemoveThenAddPermaBlessedCommitStores(addresses(COMMIT_STORE_1), addresses(COMMIT_STORE_2)); - assertFalse(s_rmn.isBlessed(taggedRootCommitStore1)); - assertTrue(s_rmn.isBlessed(taggedRootCommitStore2)); - assertEq(s_rmn.getPermaBlessedCommitStores(), addresses(COMMIT_STORE_2)); - - vm.prank(OWNER); - s_rmn.ownerRemoveThenAddPermaBlessedCommitStores(addresses(), addresses(COMMIT_STORE_1)); - assertTrue(s_rmn.isBlessed(taggedRootCommitStore1)); - assertTrue(s_rmn.isBlessed(taggedRootCommitStore2)); - assertEq(s_rmn.getPermaBlessedCommitStores(), addresses(COMMIT_STORE_2, COMMIT_STORE_1)); - - vm.prank(OWNER); - s_rmn.ownerRemoveThenAddPermaBlessedCommitStores(addresses(COMMIT_STORE_1, COMMIT_STORE_2), addresses()); - assertFalse(s_rmn.isBlessed(taggedRootCommitStore1)); - assertFalse(s_rmn.isBlessed(taggedRootCommitStore2)); - assertEq(s_rmn.getPermaBlessedCommitStores(), addresses()); - } -} - -contract RMN_getRecordedCurseRelatedOps is RMNSetup { - function test_OpsPostDeployment() public view { - // The constructor call includes a setConfig, so that's the only thing we should expect to find. - assertEq(s_rmn.getRecordedCurseRelatedOpsCount(), 1); - RMN.RecordedCurseRelatedOp[] memory recordedCurseRelatedOps = s_rmn.getRecordedCurseRelatedOps(0, type(uint256).max); - assertEq(recordedCurseRelatedOps.length, 1); - assertEq(uint8(recordedCurseRelatedOps[0].tag), uint8(RMN.RecordedCurseRelatedOpTag.SetConfig)); - } -} diff --git a/contracts/src/v0.8/ccip/test/arm/RMNSetup.t.sol b/contracts/src/v0.8/ccip/test/arm/RMNSetup.t.sol deleted file mode 100644 index 8feacb95f45..00000000000 --- a/contracts/src/v0.8/ccip/test/arm/RMNSetup.t.sol +++ /dev/null @@ -1,144 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {RMN} from "../../RMN.sol"; -import {IRMN} from "../../interfaces/IRMN.sol"; - -import {Test} from "forge-std/Test.sol"; - -function makeSubjects(bytes16 a) pure returns (bytes16[] memory) { - bytes16[] memory subjects = new bytes16[](1); - subjects[0] = a; - return subjects; -} - -function makeSubjects(bytes16 a, bytes16 b) pure returns (bytes16[] memory) { - bytes16[] memory subjects = new bytes16[](2); - subjects[0] = a; - subjects[1] = b; - return subjects; -} - -// in order from earliest to latest curse ids -function makeCursesHashFromList(bytes32[] memory curseIds) pure returns (bytes28 cursesHash) { - for (uint256 i = 0; i < curseIds.length; ++i) { - cursesHash = bytes28(keccak256(abi.encode(cursesHash, curseIds[i]))); - } -} - -// hides the ugliness from tests -function makeCursesHash(bytes32 a) pure returns (bytes28) { - bytes32[] memory curseIds = new bytes32[](1); - curseIds[0] = a; - return makeCursesHashFromList(curseIds); -} - -function makeCursesHash(bytes32 a, bytes32 b) pure returns (bytes28) { - bytes32[] memory curseIds = new bytes32[](2); - curseIds[0] = a; - curseIds[1] = b; - return makeCursesHashFromList(curseIds); -} - -contract RMNSetup is Test { - // Addresses - address internal constant OWNER = 0x00007e64E1fB0C487F25dd6D3601ff6aF8d32e4e; - address internal constant STRANGER = address(999999); - address internal constant ZERO_ADDRESS = address(0); - address internal constant BLESS_VOTER_1 = address(1); - address internal constant CURSE_VOTER_1 = address(10); - address internal constant BLESS_VOTER_2 = address(2); - address internal constant CURSE_VOTER_2 = address(12); - address internal constant BLESS_VOTER_3 = address(3); - address internal constant CURSE_VOTER_3 = address(13); - address internal constant BLESS_VOTER_4 = address(4); - address internal constant CURSE_VOTER_4 = address(14); - - // Arm - function rmnConstructorArgs() internal pure returns (RMN.Config memory) { - RMN.Voter[] memory voters = new RMN.Voter[](4); - voters[0] = RMN.Voter({ - blessVoteAddr: BLESS_VOTER_1, - curseVoteAddr: CURSE_VOTER_1, - blessWeight: WEIGHT_1, - curseWeight: WEIGHT_1 - }); - voters[1] = RMN.Voter({ - blessVoteAddr: BLESS_VOTER_2, - curseVoteAddr: CURSE_VOTER_2, - blessWeight: WEIGHT_10, - curseWeight: WEIGHT_10 - }); - voters[2] = RMN.Voter({ - blessVoteAddr: BLESS_VOTER_3, - curseVoteAddr: CURSE_VOTER_3, - blessWeight: WEIGHT_20, - curseWeight: WEIGHT_20 - }); - voters[3] = RMN.Voter({ - blessVoteAddr: BLESS_VOTER_4, - curseVoteAddr: CURSE_VOTER_4, - blessWeight: WEIGHT_40, - curseWeight: WEIGHT_40 - }); - return RMN.Config({ - voters: voters, - blessWeightThreshold: WEIGHT_10 + WEIGHT_20 + WEIGHT_40, - curseWeightThreshold: WEIGHT_1 + WEIGHT_10 + WEIGHT_20 + WEIGHT_40 - }); - } - - uint8 internal constant ZERO = 0; - uint8 internal constant WEIGHT_1 = 1; - uint8 internal constant WEIGHT_10 = 10; - uint8 internal constant WEIGHT_20 = 20; - uint8 internal constant WEIGHT_40 = 40; - - function makeTaggedRootsInclusive(uint256 from, uint256 to) internal pure returns (IRMN.TaggedRoot[] memory) { - IRMN.TaggedRoot[] memory votes = new IRMN.TaggedRoot[](to - from + 1); - for (uint256 i = from; i <= to; ++i) { - votes[i - from] = IRMN.TaggedRoot({commitStore: address(1), root: bytes32(uint256(i))}); - } - return votes; - } - - function makeTaggedRootSingleton(uint256 index) internal pure returns (IRMN.TaggedRoot[] memory) { - return makeTaggedRootsInclusive(index, index); - } - - function makeTaggedRoot(uint256 index) internal pure returns (IRMN.TaggedRoot memory) { - return makeTaggedRootSingleton(index)[0]; - } - - function makeTaggedRootHash(uint256 index) internal pure returns (bytes32) { - IRMN.TaggedRoot memory taggedRoot = makeTaggedRootSingleton(index)[0]; - return keccak256(abi.encode(taggedRoot.commitStore, taggedRoot.root)); - } - - function makeCurseId(uint256 index) internal pure returns (bytes16) { - return bytes16(uint128(index)); - } - - RMN internal s_rmn; - - function setUp() public virtual { - vm.startPrank(OWNER); - s_rmn = new RMN(rmnConstructorArgs()); - vm.stopPrank(); - } - - function hasVotedToBlessRoot(address voter, IRMN.TaggedRoot memory taggedRoot_) internal view returns (bool) { - (address[] memory voters,,) = s_rmn.getBlessProgress(taggedRoot_); - for (uint256 i = 0; i < voters.length; ++i) { - if (voters[i] == voter) { - return true; - } - } - return false; - } - - function getWeightOfVotesToBlessRoot(IRMN.TaggedRoot memory taggedRoot_) internal view returns (uint16) { - (, uint16 weight,) = s_rmn.getBlessProgress(taggedRoot_); - return weight; - } -} diff --git a/contracts/src/v0.8/ccip/test/arm/RMN_benchmark.t.sol b/contracts/src/v0.8/ccip/test/arm/RMN_benchmark.t.sol deleted file mode 100644 index 8564614a748..00000000000 --- a/contracts/src/v0.8/ccip/test/arm/RMN_benchmark.t.sol +++ /dev/null @@ -1,217 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {GLOBAL_CURSE_SUBJECT, OWNER_CURSE_VOTE_ADDR, RMN} from "../../RMN.sol"; -import {RMNSetup, makeCursesHash, makeSubjects} from "./RMNSetup.t.sol"; - -contract RMN_voteToBless_Benchmark is RMNSetup { - function test_RootSuccess_gas(uint256 n) internal { - vm.prank(BLESS_VOTER_1); - s_rmn.voteToBless(makeTaggedRootsInclusive(1, n)); - } - - function test_1RootSuccess_gas() public { - test_RootSuccess_gas(1); - } - - function test_3RootSuccess_gas() public { - test_RootSuccess_gas(3); - } - - function test_5RootSuccess_gas() public { - test_RootSuccess_gas(5); - } -} - -contract RMN_voteToBless_Blessed_Benchmark is RMN_voteToBless_Benchmark { - function setUp() public virtual override { - RMNSetup.setUp(); - vm.prank(BLESS_VOTER_2); - s_rmn.voteToBless(makeTaggedRootsInclusive(1, 1)); - vm.prank(BLESS_VOTER_3); - s_rmn.voteToBless(makeTaggedRootsInclusive(1, 1)); - } - - function test_1RootSuccessBecameBlessed_gas() public { - vm.prank(BLESS_VOTER_4); - s_rmn.voteToBless(makeTaggedRootsInclusive(1, 1)); - } -} - -abstract contract RMN_voteToCurse_Benchmark is RMNSetup { - struct PreVote { - address voter; - bytes16 subject; - } - - PreVote[] internal s_preVotes; - - function setUp() public virtual override { - // Intentionally does not inherit RMNSetup setUp(), because we set up a simpler config here. - // The only way to ensure that storage slots are cold for the actual functions to be benchmarked is to perform the - // setup in setUp(). - - RMN.Config memory cfg = RMN.Config({voters: new RMN.Voter[](3), blessWeightThreshold: 3, curseWeightThreshold: 3}); - cfg.voters[0] = - RMN.Voter({blessVoteAddr: BLESS_VOTER_1, curseVoteAddr: CURSE_VOTER_1, blessWeight: 1, curseWeight: 1}); - cfg.voters[1] = - RMN.Voter({blessVoteAddr: BLESS_VOTER_2, curseVoteAddr: CURSE_VOTER_2, blessWeight: 1, curseWeight: 1}); - cfg.voters[2] = - RMN.Voter({blessVoteAddr: BLESS_VOTER_3, curseVoteAddr: CURSE_VOTER_3, blessWeight: 1, curseWeight: 1}); - vm.prank(OWNER); - s_rmn = new RMN(cfg); - - for (uint256 i = 0; i < s_preVotes.length; ++i) { - vm.prank(s_preVotes[i].voter); - s_rmn.voteToCurse(makeCurseId(i), makeSubjects(s_preVotes[i].subject)); - } - } -} - -contract RMN_voteToCurse_Benchmark_1 is RMN_voteToCurse_Benchmark { - constructor() { - // some irrelevant subject & voter so that we don't pay for the nonzero->zero SSTORE of - // s_recordedVotesToCurse.length in the benchmark below - s_preVotes.push(PreVote({voter: CURSE_VOTER_3, subject: bytes16(~uint128(0))})); - } - - function test_VoteToCurse_NewSubject_NewVoter_NoCurse_gas() public { - vm.prank(CURSE_VOTER_1); - s_rmn.voteToCurse(makeCurseId(0xffff), makeSubjects(GLOBAL_CURSE_SUBJECT)); - } - - function test_VoteToCurse_NewSubject_NewVoter_YesCurse_gas() public { - vm.prank(OWNER); - s_rmn.ownerCurse(makeCurseId(0xffff), makeSubjects(GLOBAL_CURSE_SUBJECT)); - } -} - -contract RMN_voteToCurse_Benchmark_2 is RMN_voteToCurse_Benchmark { - constructor() { - s_preVotes.push(PreVote({voter: CURSE_VOTER_1, subject: GLOBAL_CURSE_SUBJECT})); - } - - function test_VoteToCurse_OldSubject_OldVoter_NoCurse_gas() public { - vm.prank(CURSE_VOTER_1); - s_rmn.voteToCurse(makeCurseId(0xffff), makeSubjects(GLOBAL_CURSE_SUBJECT)); - } - - function test_VoteToCurse_OldSubject_NewVoter_NoCurse_gas() public { - vm.prank(CURSE_VOTER_2); - s_rmn.voteToCurse(makeCurseId(0xffff), makeSubjects(GLOBAL_CURSE_SUBJECT)); - } -} - -contract RMN_voteToCurse_Benchmark_3 is RMN_voteToCurse_Benchmark { - constructor() { - s_preVotes.push(PreVote({voter: CURSE_VOTER_1, subject: GLOBAL_CURSE_SUBJECT})); - s_preVotes.push(PreVote({voter: CURSE_VOTER_2, subject: GLOBAL_CURSE_SUBJECT})); - } - - function test_VoteToCurse_OldSubject_NewVoter_YesCurse_gas() public { - vm.prank(CURSE_VOTER_3); - s_rmn.voteToCurse(makeCurseId(0xffff), makeSubjects(GLOBAL_CURSE_SUBJECT)); - } -} - -contract RMN_lazyVoteToCurseUpdate_Benchmark is RMN_voteToCurse_Benchmark { - constructor() { - s_preVotes.push(PreVote({voter: CURSE_VOTER_1, subject: GLOBAL_CURSE_SUBJECT})); - s_preVotes.push(PreVote({voter: CURSE_VOTER_2, subject: GLOBAL_CURSE_SUBJECT})); - s_preVotes.push(PreVote({voter: CURSE_VOTER_3, subject: GLOBAL_CURSE_SUBJECT})); - } - - function setUp() public override { - RMN_voteToCurse_Benchmark.setUp(); // sends the prevotes - // initial config includes voters CURSE_VOTER_1, CURSE_VOTER_2, CURSE_VOTER_3 - // include a new voter in the config - { - (,, RMN.Config memory cfg) = s_rmn.getConfigDetails(); - RMN.Voter[] memory newVoters = new RMN.Voter[](cfg.voters.length + 1); - for (uint256 i = 0; i < cfg.voters.length; ++i) { - newVoters[i] = cfg.voters[i]; - } - newVoters[newVoters.length - 1] = - RMN.Voter({blessVoteAddr: BLESS_VOTER_4, curseVoteAddr: CURSE_VOTER_4, blessWeight: 1, curseWeight: 1}); - cfg.voters = newVoters; - - vm.prank(OWNER); - s_rmn.setConfig(cfg); - } - } - - function test_VoteToCurseLazilyRetain3VotersUponConfigChange_gas() public { - // send a vote as the new voter, should cause a lazy update and votes from CURSE_VOTER_1, CURSE_VOTER_2, - // CURSE_VOTER_3 to be retained, which is the worst case for the prior config - vm.prank(CURSE_VOTER_4); - s_rmn.voteToCurse(makeCurseId(0xffff), makeSubjects(GLOBAL_CURSE_SUBJECT)); - } -} - -contract RMN_setConfig_Benchmark is RMNSetup { - uint256 s_numVoters; - - function configWithVoters(uint256 numVoters) internal pure returns (RMN.Config memory) { - RMN.Config memory cfg = - RMN.Config({voters: new RMN.Voter[](numVoters), blessWeightThreshold: 1, curseWeightThreshold: 1}); - for (uint256 i = 1; i <= numVoters; ++i) { - cfg.voters[i - 1] = RMN.Voter({ - blessVoteAddr: address(uint160(2 * i)), - curseVoteAddr: address(uint160(2 * i + 1)), - blessWeight: 1, - curseWeight: 1 - }); - } - return cfg; - } - - function setUp() public virtual override { - vm.prank(OWNER); - s_rmn = new RMN(configWithVoters(s_numVoters)); - } -} - -contract RMN_setConfig_Benchmark_1 is RMN_setConfig_Benchmark { - constructor() { - s_numVoters = 1; - } - - function test_SetConfig_7Voters_gas() public { - vm.prank(OWNER); - s_rmn.setConfig(configWithVoters(7)); - } -} - -contract RMN_setConfig_Benchmark_2 is RMN_setConfig_Benchmark { - constructor() { - s_numVoters = 7; - } - - function test_ResetConfig_7Voters_gas() public { - vm.prank(OWNER); - s_rmn.setConfig(configWithVoters(7)); - } -} - -contract RMN_ownerUnvoteToCurse_Benchmark is RMN_setConfig_Benchmark { - constructor() { - s_numVoters = 7; - } - - function setUp() public override { - RMN_setConfig_Benchmark.setUp(); - vm.prank(OWNER); - s_rmn.ownerCurse(makeCurseId(0xffff), makeSubjects(GLOBAL_CURSE_SUBJECT)); - } - - function test_OwnerUnvoteToCurse_1Voter_LiftsCurse_gas() public { - RMN.OwnerUnvoteToCurseRequest[] memory reqs = new RMN.OwnerUnvoteToCurseRequest[](1); - reqs[0] = RMN.OwnerUnvoteToCurseRequest({ - curseVoteAddr: OWNER_CURSE_VOTE_ADDR, - unit: RMN.UnvoteToCurseRequest({cursesHash: makeCursesHash(makeCurseId(0xffff)), subject: GLOBAL_CURSE_SUBJECT}), - forceUnvote: false - }); - vm.prank(OWNER); - s_rmn.ownerUnvoteToCurse(reqs); - } -} diff --git a/contracts/src/v0.8/ccip/test/attacks/onRamp/MultiOnRampTokenPoolReentrancy.t.sol b/contracts/src/v0.8/ccip/test/attacks/onRamp/MultiOnRampTokenPoolReentrancy.t.sol deleted file mode 100644 index 4700d7edea2..00000000000 --- a/contracts/src/v0.8/ccip/test/attacks/onRamp/MultiOnRampTokenPoolReentrancy.t.sol +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {Client} from "../../../libraries/Client.sol"; -import {Internal} from "../../../libraries/Internal.sol"; -import {OnRamp} from "../../../onRamp/OnRamp.sol"; -import {TokenPool} from "../../../pools/TokenPool.sol"; -import {OnRampSetup} from "../../onRamp/OnRampSetup.t.sol"; -import {FacadeClient} from "./FacadeClient.sol"; -import {ReentrantMaliciousTokenPool} from "./ReentrantMaliciousTokenPool.sol"; - -import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; - -/// @title MultiOnRampTokenPoolReentrancy -/// Attempts to perform a reentrancy exploit on Onramp with a malicious TokenPool -contract MultiOnRampTokenPoolReentrancy is OnRampSetup { - FacadeClient internal s_facadeClient; - ReentrantMaliciousTokenPool internal s_maliciousTokenPool; - IERC20 internal s_sourceToken; - IERC20 internal s_feeToken; - address internal immutable i_receiver = makeAddr("receiver"); - - function setUp() public virtual override { - OnRampSetup.setUp(); - - s_sourceToken = IERC20(s_sourceTokens[0]); - s_feeToken = IERC20(s_sourceTokens[0]); - - s_facadeClient = - new FacadeClient(address(s_sourceRouter), DEST_CHAIN_SELECTOR, s_sourceToken, s_feeToken, i_receiver); - - s_maliciousTokenPool = new ReentrantMaliciousTokenPool( - address(s_facadeClient), s_sourceToken, address(s_mockRMN), address(s_sourceRouter) - ); - - TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](1); - chainUpdates[0] = TokenPool.ChainUpdate({ - remoteChainSelector: DEST_CHAIN_SELECTOR, - remotePoolAddress: abi.encode(s_destPoolBySourceToken[s_sourceTokens[0]]), - remoteTokenAddress: abi.encode(s_destTokens[0]), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - s_maliciousTokenPool.applyChainUpdates(chainUpdates); - s_sourcePoolByToken[address(s_sourceToken)] = address(s_maliciousTokenPool); - - Internal.PoolUpdate[] memory removes = new Internal.PoolUpdate[](1); - removes[0].token = address(s_sourceToken); - removes[0].pool = address(s_sourcePoolByToken[address(s_sourceToken)]); - Internal.PoolUpdate[] memory adds = new Internal.PoolUpdate[](1); - adds[0].token = address(s_sourceToken); - adds[0].pool = address(s_maliciousTokenPool); - - s_tokenAdminRegistry.setPool(address(s_sourceToken), address(s_maliciousTokenPool)); - - s_sourceToken.transfer(address(s_facadeClient), 1e18); - s_feeToken.transfer(address(s_facadeClient), 1e18); - } - - /// @dev This test was used to showcase a reentrancy exploit on OnRamp with malicious TokenPool. - /// How it worked: OnRamp used to construct EVM2Any messages after calling TokenPool's lockOrBurn. - /// This allowed the malicious TokenPool to break message sequencing expectations as follows: - /// Any user -> Facade -> 1st call to ccipSend -> pool’s lockOrBurn —> - /// (reenter)-> Facade -> 2nd call to ccipSend - /// In this case, Facade's second call would produce an EVM2Any msg with a lower sequence number. - /// The issue was fixed by implementing a reentrancy guard in OnRamp. - function test_OnRampTokenPoolReentrancy_Success() public { - uint256 amount = 1; - - Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); - tokenAmounts[0].token = address(s_sourceToken); - tokenAmounts[0].amount = amount; - - Client.EVM2AnyMessage memory message1 = Client.EVM2AnyMessage({ - receiver: abi.encode(i_receiver), - data: abi.encodePacked(uint256(1)), // message 1 contains data 1 - tokenAmounts: tokenAmounts, - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 200_000})), - feeToken: address(s_feeToken) - }); - - uint256 expectedFee = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, message1); - assertGt(expectedFee, 0); - - // Outcome of a successful exploit: - // Message 1 event from OnRamp contains sequence/nonce 2, message 2 contains sequence/nonce 1 - // Internal.EVM2EVMMessage memory msgEvent1 = _messageToEvent(message1, 2, 2, expectedFee, address(s_facadeClient)); - // Internal.EVM2EVMMessage memory msgEvent2 = _messageToEvent(message2, 1, 1, expectedFee, address(s_facadeClient)); - - vm.expectRevert(OnRamp.ReentrancyGuardReentrantCall.selector); - s_facadeClient.send(amount); - } -} diff --git a/contracts/src/v0.8/ccip/test/attacks/onRamp/OnRampTokenPoolReentrancy.t.sol b/contracts/src/v0.8/ccip/test/attacks/onRamp/OnRampTokenPoolReentrancy.t.sol index 03db40a2931..0c1cc714be9 100644 --- a/contracts/src/v0.8/ccip/test/attacks/onRamp/OnRampTokenPoolReentrancy.t.sol +++ b/contracts/src/v0.8/ccip/test/attacks/onRamp/OnRampTokenPoolReentrancy.t.sol @@ -3,17 +3,17 @@ pragma solidity 0.8.24; import {Client} from "../../../libraries/Client.sol"; import {Internal} from "../../../libraries/Internal.sol"; -import {EVM2EVMOnRamp} from "../../../onRamp/EVM2EVMOnRamp.sol"; +import {OnRamp} from "../../../onRamp/OnRamp.sol"; import {TokenPool} from "../../../pools/TokenPool.sol"; -import {EVM2EVMOnRampSetup} from "../../onRamp/EVM2EVMOnRampSetup.t.sol"; +import {OnRampSetup} from "../../onRamp/OnRampSetup.t.sol"; import {FacadeClient} from "./FacadeClient.sol"; import {ReentrantMaliciousTokenPool} from "./ReentrantMaliciousTokenPool.sol"; import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -/// @title OnRampTokenPoolReentrancy +/// @title MultiOnRampTokenPoolReentrancy /// Attempts to perform a reentrancy exploit on Onramp with a malicious TokenPool -contract OnRampTokenPoolReentrancy is EVM2EVMOnRampSetup { +contract OnRampTokenPoolReentrancy is OnRampSetup { FacadeClient internal s_facadeClient; ReentrantMaliciousTokenPool internal s_maliciousTokenPool; IERC20 internal s_sourceToken; @@ -21,7 +21,7 @@ contract OnRampTokenPoolReentrancy is EVM2EVMOnRampSetup { address internal immutable i_receiver = makeAddr("receiver"); function setUp() public virtual override { - EVM2EVMOnRampSetup.setUp(); + OnRampSetup.setUp(); s_sourceToken = IERC20(s_sourceTokens[0]); s_feeToken = IERC20(s_sourceTokens[0]); @@ -45,13 +45,6 @@ contract OnRampTokenPoolReentrancy is EVM2EVMOnRampSetup { s_maliciousTokenPool.applyChainUpdates(chainUpdates); s_sourcePoolByToken[address(s_sourceToken)] = address(s_maliciousTokenPool); - Internal.PoolUpdate[] memory removes = new Internal.PoolUpdate[](1); - removes[0].token = address(s_sourceToken); - removes[0].pool = address(s_sourcePoolByToken[address(s_sourceToken)]); - Internal.PoolUpdate[] memory adds = new Internal.PoolUpdate[](1); - adds[0].token = address(s_sourceToken); - adds[0].pool = address(s_maliciousTokenPool); - s_tokenAdminRegistry.setPool(address(s_sourceToken), address(s_maliciousTokenPool)); s_sourceToken.transfer(address(s_facadeClient), 1e18); @@ -59,13 +52,12 @@ contract OnRampTokenPoolReentrancy is EVM2EVMOnRampSetup { } /// @dev This test was used to showcase a reentrancy exploit on OnRamp with malicious TokenPool. - /// How it worked: OnRamp used to construct EVM2EVM messages after calling TokenPool's lockOrBurn. + /// How it worked: OnRamp used to construct EVM2Any messages after calling TokenPool's lockOrBurn. /// This allowed the malicious TokenPool to break message sequencing expectations as follows: /// Any user -> Facade -> 1st call to ccipSend -> pool’s lockOrBurn —> /// (reenter)-> Facade -> 2nd call to ccipSend - /// In this case, Facade's second call would produce an EVM2EVM msg with a lower sequence number. - /// The issue was fixed by moving state updates and event construction to before TokenPool calls. - /// This test is kept to verify message sequence expectations are not broken. + /// In this case, Facade's second call would produce an EVM2Any msg with a lower sequence number. + /// The issue was fixed by implementing a reentrancy guard in OnRamp. function test_OnRampTokenPoolReentrancy_Success() public { uint256 amount = 1; @@ -81,36 +73,10 @@ contract OnRampTokenPoolReentrancy is EVM2EVMOnRampSetup { feeToken: address(s_feeToken) }); - Client.EVM2AnyMessage memory message2 = Client.EVM2AnyMessage({ - receiver: abi.encode(i_receiver), - data: abi.encodePacked(uint256(2)), // message 2 contains data 2 - tokenAmounts: tokenAmounts, - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 200_000})), - feeToken: address(s_feeToken) - }); - uint256 expectedFee = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, message1); assertGt(expectedFee, 0); - // Outcome of a successful exploit: - // Message 1 event from OnRamp contains sequence/nonce 2, message 2 contains sequence/nonce 1 - // Internal.EVM2EVMMessage memory msgEvent1 = _messageToEvent(message1, 2, 2, expectedFee, address(s_facadeClient)); - // Internal.EVM2EVMMessage memory msgEvent2 = _messageToEvent(message2, 1, 1, expectedFee, address(s_facadeClient)); - - // vm.expectEmit(); - // emit CCIPSendRequested(msgEvent2); - // vm.expectEmit(); - // emit CCIPSendRequested(msgEvent1); - - // After issue is fixed, sequence now increments as expected - Internal.EVM2EVMMessage memory msgEvent1 = _messageToEvent(message1, 1, 1, expectedFee, address(s_facadeClient)); - Internal.EVM2EVMMessage memory msgEvent2 = _messageToEvent(message2, 2, 2, expectedFee, address(s_facadeClient)); - - vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(msgEvent2); - vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(msgEvent1); - + vm.expectRevert(OnRamp.ReentrancyGuardReentrantCall.selector); s_facadeClient.send(amount); } } diff --git a/contracts/src/v0.8/ccip/test/attacks/onRamp/ReentrantMaliciousTokenPool.sol b/contracts/src/v0.8/ccip/test/attacks/onRamp/ReentrantMaliciousTokenPool.sol index 5b6b3679848..f50f233e737 100644 --- a/contracts/src/v0.8/ccip/test/attacks/onRamp/ReentrantMaliciousTokenPool.sol +++ b/contracts/src/v0.8/ccip/test/attacks/onRamp/ReentrantMaliciousTokenPool.sol @@ -8,7 +8,7 @@ import {FacadeClient} from "./FacadeClient.sol"; import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; contract ReentrantMaliciousTokenPool is TokenPool { - address private i_facade; + address private immutable i_facade; bool private s_attacked; @@ -32,6 +32,7 @@ contract ReentrantMaliciousTokenPool is TokenPool { s_attacked = true; + // solhint-disable-next-line check-send-result FacadeClient(i_facade).send(lockOrBurnIn.amount); emit Burned(msg.sender, lockOrBurnIn.amount); return Pool.LockOrBurnOutV1({destTokenAddress: getRemoteToken(lockOrBurnIn.remoteChainSelector), destPoolData: ""}); diff --git a/contracts/src/v0.8/ccip/test/commitStore/CommitStore.t.sol b/contracts/src/v0.8/ccip/test/commitStore/CommitStore.t.sol deleted file mode 100644 index 0976ab96c5e..00000000000 --- a/contracts/src/v0.8/ccip/test/commitStore/CommitStore.t.sol +++ /dev/null @@ -1,615 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {IPriceRegistry} from "../../interfaces/IPriceRegistry.sol"; -import {IRMN} from "../../interfaces/IRMN.sol"; - -import {AuthorizedCallers} from "../../../shared/access/AuthorizedCallers.sol"; -import {CommitStore} from "../../CommitStore.sol"; -import {FeeQuoter} from "../../FeeQuoter.sol"; -import {RMN} from "../../RMN.sol"; -import {MerkleMultiProof} from "../../libraries/MerkleMultiProof.sol"; -import {OCR2Abstract} from "../../ocr/OCR2Abstract.sol"; -import {FeeQuoterSetup} from "../feeQuoter/FeeQuoterSetup.t.sol"; -import {CommitStoreHelper} from "../helpers/CommitStoreHelper.sol"; -import {OCR2BaseSetup} from "../ocr/OCR2Base.t.sol"; - -contract CommitStoreSetup is FeeQuoterSetup, OCR2BaseSetup { - CommitStoreHelper internal s_commitStore; - - function setUp() public virtual override(FeeQuoterSetup, OCR2BaseSetup) { - FeeQuoterSetup.setUp(); - OCR2BaseSetup.setUp(); - - s_commitStore = new CommitStoreHelper( - CommitStore.StaticConfig({ - chainSelector: DEST_CHAIN_SELECTOR, - sourceChainSelector: SOURCE_CHAIN_SELECTOR, - onRamp: ON_RAMP_ADDRESS, - rmnProxy: address(s_mockRMN) - }) - ); - CommitStore.DynamicConfig memory dynamicConfig = CommitStore.DynamicConfig({priceRegistry: address(s_feeQuoter)}); - s_commitStore.setOCR2Config( - s_valid_signers, s_valid_transmitters, s_f, abi.encode(dynamicConfig), s_offchainConfigVersion, abi.encode("") - ); - - address[] memory priceUpdaters = new address[](1); - priceUpdaters[0] = address(s_commitStore); - s_feeQuoter.applyAuthorizedCallerUpdates( - AuthorizedCallers.AuthorizedCallerArgs({addedCallers: priceUpdaters, removedCallers: new address[](0)}) - ); - } -} - -contract CommitStoreRealRMNSetup is FeeQuoterSetup, OCR2BaseSetup { - CommitStoreHelper internal s_commitStore; - - RMN internal s_rmn; - - address internal constant BLESS_VOTE_ADDR = address(8888); - - function setUp() public virtual override(FeeQuoterSetup, OCR2BaseSetup) { - FeeQuoterSetup.setUp(); - OCR2BaseSetup.setUp(); - - RMN.Voter[] memory voters = new RMN.Voter[](1); - voters[0] = - RMN.Voter({blessVoteAddr: BLESS_VOTE_ADDR, curseVoteAddr: address(9999), blessWeight: 1, curseWeight: 1}); - // Overwrite base mock rmn with real. - s_rmn = new RMN(RMN.Config({voters: voters, blessWeightThreshold: 1, curseWeightThreshold: 1})); - s_commitStore = new CommitStoreHelper( - CommitStore.StaticConfig({ - chainSelector: DEST_CHAIN_SELECTOR, - sourceChainSelector: SOURCE_CHAIN_SELECTOR, - onRamp: ON_RAMP_ADDRESS, - rmnProxy: address(s_rmn) - }) - ); - CommitStore.DynamicConfig memory dynamicConfig = CommitStore.DynamicConfig({priceRegistry: address(s_feeQuoter)}); - s_commitStore.setOCR2Config( - s_valid_signers, s_valid_transmitters, s_f, abi.encode(dynamicConfig), s_offchainConfigVersion, abi.encode("") - ); - } -} - -contract CommitStore_constructor is FeeQuoterSetup, OCR2BaseSetup { - function setUp() public virtual override(FeeQuoterSetup, OCR2BaseSetup) { - FeeQuoterSetup.setUp(); - OCR2BaseSetup.setUp(); - } - - function test_Constructor_Success() public { - CommitStore.StaticConfig memory staticConfig = CommitStore.StaticConfig({ - chainSelector: DEST_CHAIN_SELECTOR, - sourceChainSelector: SOURCE_CHAIN_SELECTOR, - onRamp: 0x2C44CDDdB6a900Fa2B585dd299E03D12Fa4293Bc, - rmnProxy: address(s_mockRMN) - }); - CommitStore.DynamicConfig memory dynamicConfig = CommitStore.DynamicConfig({priceRegistry: address(s_feeQuoter)}); - - vm.expectEmit(); - emit CommitStore.ConfigSet(staticConfig, dynamicConfig); - - CommitStore commitStore = new CommitStore(staticConfig); - commitStore.setOCR2Config( - s_valid_signers, s_valid_transmitters, s_f, abi.encode(dynamicConfig), s_offchainConfigVersion, abi.encode("") - ); - - CommitStore.StaticConfig memory gotStaticConfig = commitStore.getStaticConfig(); - - assertEq(staticConfig.chainSelector, gotStaticConfig.chainSelector); - assertEq(staticConfig.sourceChainSelector, gotStaticConfig.sourceChainSelector); - assertEq(staticConfig.onRamp, gotStaticConfig.onRamp); - assertEq(staticConfig.rmnProxy, gotStaticConfig.rmnProxy); - - CommitStore.DynamicConfig memory gotDynamicConfig = commitStore.getDynamicConfig(); - - assertEq(dynamicConfig.priceRegistry, gotDynamicConfig.priceRegistry); - - // CommitStore initial values - assertEq(0, commitStore.getLatestPriceEpochAndRound()); - assertEq(1, commitStore.getExpectedNextSequenceNumber()); - assertEq(commitStore.typeAndVersion(), "CommitStore 1.5.0"); - assertEq(OWNER, commitStore.owner()); - assertTrue(commitStore.isUnpausedAndNotCursed()); - } -} - -contract CommitStore_setMinSeqNr is CommitStoreSetup { - function test_Fuzz_SetMinSeqNr_Success(uint64 minSeqNr) public { - vm.expectEmit(); - emit CommitStore.SequenceNumberSet(s_commitStore.getExpectedNextSequenceNumber(), minSeqNr); - - s_commitStore.setMinSeqNr(minSeqNr); - - assertEq(s_commitStore.getExpectedNextSequenceNumber(), minSeqNr); - } - - // Reverts - function test_OnlyOwner_Revert() public { - vm.stopPrank(); - vm.expectRevert("Only callable by owner"); - s_commitStore.setMinSeqNr(6723); - } -} - -contract CommitStore_setDynamicConfig is CommitStoreSetup { - function test_Fuzz_SetDynamicConfig_Success(address priceRegistry) public { - vm.assume(priceRegistry != address(0)); - CommitStore.StaticConfig memory staticConfig = s_commitStore.getStaticConfig(); - CommitStore.DynamicConfig memory dynamicConfig = CommitStore.DynamicConfig({priceRegistry: priceRegistry}); - bytes memory onchainConfig = abi.encode(dynamicConfig); - - vm.expectEmit(); - emit CommitStore.ConfigSet(staticConfig, dynamicConfig); - - uint32 configCount = 1; - - vm.expectEmit(); - emit OCR2Abstract.ConfigSet( - uint32(block.number), - getBasicConfigDigest(address(s_commitStore), s_f, configCount, onchainConfig), - configCount + 1, - s_valid_signers, - s_valid_transmitters, - s_f, - onchainConfig, - s_offchainConfigVersion, - abi.encode("") - ); - - s_commitStore.setOCR2Config( - s_valid_signers, s_valid_transmitters, s_f, onchainConfig, s_offchainConfigVersion, abi.encode("") - ); - - CommitStore.DynamicConfig memory gotDynamicConfig = s_commitStore.getDynamicConfig(); - assertEq(gotDynamicConfig.priceRegistry, dynamicConfig.priceRegistry); - } - - function test_PriceEpochCleared_Success() public { - // Set latest price epoch and round to non-zero. - uint40 latestEpochAndRound = 1782155; - s_commitStore.setLatestPriceEpochAndRound(latestEpochAndRound); - assertEq(latestEpochAndRound, s_commitStore.getLatestPriceEpochAndRound()); - - CommitStore.DynamicConfig memory dynamicConfig = CommitStore.DynamicConfig({priceRegistry: address(1)}); - // New config should clear it. - s_commitStore.setOCR2Config( - s_valid_signers, s_valid_transmitters, s_f, abi.encode(dynamicConfig), s_offchainConfigVersion, abi.encode("") - ); - // Assert cleared. - assertEq(0, s_commitStore.getLatestPriceEpochAndRound()); - } - - // Reverts - function test_OnlyOwner_Revert() public { - CommitStore.DynamicConfig memory dynamicConfig = CommitStore.DynamicConfig({priceRegistry: address(23784264)}); - - vm.stopPrank(); - vm.expectRevert("Only callable by owner"); - s_commitStore.setOCR2Config( - s_valid_signers, s_valid_transmitters, s_f, abi.encode(dynamicConfig), s_offchainConfigVersion, abi.encode("") - ); - } - - function test_InvalidCommitStoreConfig_Revert() public { - CommitStore.DynamicConfig memory dynamicConfig = CommitStore.DynamicConfig({priceRegistry: address(0)}); - - vm.expectRevert(CommitStore.InvalidCommitStoreConfig.selector); - s_commitStore.setOCR2Config( - s_valid_signers, s_valid_transmitters, s_f, abi.encode(dynamicConfig), s_offchainConfigVersion, abi.encode("") - ); - } -} - -contract CommitStore_resetUnblessedRoots is CommitStoreRealRMNSetup { - function test_ResetUnblessedRoots_Success() public { - bytes32[] memory rootsToReset = new bytes32[](3); - rootsToReset[0] = "1"; - rootsToReset[1] = "2"; - rootsToReset[2] = "3"; - - CommitStore.CommitReport memory report = CommitStore.CommitReport({ - priceUpdates: _getEmptyPriceUpdates(), - interval: CommitStore.Interval(1, 2), - merkleRoot: rootsToReset[0] - }); - - s_commitStore.report(abi.encode(report), ++s_latestEpochAndRound); - - report = CommitStore.CommitReport({ - priceUpdates: _getEmptyPriceUpdates(), - interval: CommitStore.Interval(3, 4), - merkleRoot: rootsToReset[1] - }); - - s_commitStore.report(abi.encode(report), ++s_latestEpochAndRound); - - report = CommitStore.CommitReport({ - priceUpdates: _getEmptyPriceUpdates(), - interval: CommitStore.Interval(5, 5), - merkleRoot: rootsToReset[2] - }); - - s_commitStore.report(abi.encode(report), ++s_latestEpochAndRound); - - IRMN.TaggedRoot[] memory blessedTaggedRoots = new IRMN.TaggedRoot[](1); - blessedTaggedRoots[0] = IRMN.TaggedRoot({commitStore: address(s_commitStore), root: rootsToReset[1]}); - - vm.startPrank(BLESS_VOTE_ADDR); - s_rmn.voteToBless(blessedTaggedRoots); - - vm.expectEmit(false, false, false, true); - emit CommitStore.RootRemoved(rootsToReset[0]); - - vm.expectEmit(false, false, false, true); - emit CommitStore.RootRemoved(rootsToReset[2]); - - vm.startPrank(OWNER); - s_commitStore.resetUnblessedRoots(rootsToReset); - - assertEq(0, s_commitStore.getMerkleRoot(rootsToReset[0])); - assertEq(BLOCK_TIME, s_commitStore.getMerkleRoot(rootsToReset[1])); - assertEq(0, s_commitStore.getMerkleRoot(rootsToReset[2])); - } - - // Reverts - - function test_OnlyOwner_Revert() public { - vm.stopPrank(); - vm.expectRevert("Only callable by owner"); - bytes32[] memory rootToReset; - s_commitStore.resetUnblessedRoots(rootToReset); - } -} - -contract CommitStore_report is CommitStoreSetup { - function test_ReportOnlyRootSuccess_gas() public { - vm.pauseGasMetering(); - uint64 max1 = 931; - bytes32 root = "Only a single root"; - CommitStore.CommitReport memory report = CommitStore.CommitReport({ - priceUpdates: _getEmptyPriceUpdates(), - interval: CommitStore.Interval(1, max1), - merkleRoot: root - }); - - vm.expectEmit(); - emit CommitStore.ReportAccepted(report); - - bytes memory encodedReport = abi.encode(report); - - vm.resumeGasMetering(); - s_commitStore.report(encodedReport, ++s_latestEpochAndRound); - vm.pauseGasMetering(); - - assertEq(max1 + 1, s_commitStore.getExpectedNextSequenceNumber()); - assertEq(block.timestamp, s_commitStore.getMerkleRoot(root)); - vm.resumeGasMetering(); - } - - function test_ReportAndPriceUpdate_Success() public { - uint64 max1 = 12; - - CommitStore.CommitReport memory report = CommitStore.CommitReport({ - priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), - interval: CommitStore.Interval(1, max1), - merkleRoot: "test #2" - }); - - vm.expectEmit(); - emit CommitStore.ReportAccepted(report); - - s_commitStore.report(abi.encode(report), ++s_latestEpochAndRound); - - assertEq(max1 + 1, s_commitStore.getExpectedNextSequenceNumber()); - assertEq(s_latestEpochAndRound, s_commitStore.getLatestPriceEpochAndRound()); - } - - function test_StaleReportWithRoot_Success() public { - uint64 maxSeq = 12; - uint224 tokenStartPrice = - IPriceRegistry(s_commitStore.getDynamicConfig().priceRegistry).getTokenPrice(s_sourceFeeToken).value; - - CommitStore.CommitReport memory report = CommitStore.CommitReport({ - priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), - interval: CommitStore.Interval(1, maxSeq), - merkleRoot: "stale report 1" - }); - - vm.expectEmit(); - emit CommitStore.ReportAccepted(report); - - s_commitStore.report(abi.encode(report), s_latestEpochAndRound); - assertEq(maxSeq + 1, s_commitStore.getExpectedNextSequenceNumber()); - assertEq(s_latestEpochAndRound, s_commitStore.getLatestPriceEpochAndRound()); - - report = CommitStore.CommitReport({ - priceUpdates: _getEmptyPriceUpdates(), - interval: CommitStore.Interval(maxSeq + 1, maxSeq * 2), - merkleRoot: "stale report 2" - }); - - vm.expectEmit(); - emit CommitStore.ReportAccepted(report); - - s_commitStore.report(abi.encode(report), s_latestEpochAndRound); - assertEq(maxSeq * 2 + 1, s_commitStore.getExpectedNextSequenceNumber()); - assertEq(s_latestEpochAndRound, s_commitStore.getLatestPriceEpochAndRound()); - assertEq( - tokenStartPrice, - IPriceRegistry(s_commitStore.getDynamicConfig().priceRegistry).getTokenPrice(s_sourceFeeToken).value - ); - } - - function test_OnlyTokenPriceUpdates_Success() public { - CommitStore.CommitReport memory report = CommitStore.CommitReport({ - priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), - interval: CommitStore.Interval(0, 0), - merkleRoot: "" - }); - - vm.expectEmit(); - emit FeeQuoter.UsdPerTokenUpdated(s_sourceFeeToken, 4e18, block.timestamp); - - s_commitStore.report(abi.encode(report), ++s_latestEpochAndRound); - assertEq(s_latestEpochAndRound, s_commitStore.getLatestPriceEpochAndRound()); - } - - function test_OnlyGasPriceUpdates_Success() public { - CommitStore.CommitReport memory report = CommitStore.CommitReport({ - priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), - interval: CommitStore.Interval(0, 0), - merkleRoot: "" - }); - - vm.expectEmit(); - emit FeeQuoter.UsdPerTokenUpdated(s_sourceFeeToken, 4e18, block.timestamp); - - s_commitStore.report(abi.encode(report), ++s_latestEpochAndRound); - assertEq(s_latestEpochAndRound, s_commitStore.getLatestPriceEpochAndRound()); - } - - function test_ValidPriceUpdateThenStaleReportWithRoot_Success() public { - uint64 maxSeq = 12; - uint224 tokenPrice1 = 4e18; - uint224 tokenPrice2 = 5e18; - - CommitStore.CommitReport memory report = CommitStore.CommitReport({ - priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, tokenPrice1), - interval: CommitStore.Interval(0, 0), - merkleRoot: "" - }); - - vm.expectEmit(); - emit FeeQuoter.UsdPerTokenUpdated(s_sourceFeeToken, tokenPrice1, block.timestamp); - - s_commitStore.report(abi.encode(report), ++s_latestEpochAndRound); - assertEq(s_latestEpochAndRound, s_commitStore.getLatestPriceEpochAndRound()); - - report = CommitStore.CommitReport({ - priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, tokenPrice2), - interval: CommitStore.Interval(1, maxSeq), - merkleRoot: "stale report" - }); - - vm.expectEmit(); - emit CommitStore.ReportAccepted(report); - - s_commitStore.report(abi.encode(report), s_latestEpochAndRound); - - assertEq(maxSeq + 1, s_commitStore.getExpectedNextSequenceNumber()); - assertEq( - tokenPrice1, IPriceRegistry(s_commitStore.getDynamicConfig().priceRegistry).getTokenPrice(s_sourceFeeToken).value - ); - assertEq(s_latestEpochAndRound, s_commitStore.getLatestPriceEpochAndRound()); - } - - // Reverts - - function test_Paused_Revert() public { - s_commitStore.pause(); - bytes memory report; - vm.expectRevert(CommitStore.PausedError.selector); - s_commitStore.report(report, ++s_latestEpochAndRound); - } - - function test_Unhealthy_Revert() public { - s_mockRMN.setGlobalCursed(true); - vm.expectRevert(CommitStore.CursedByRMN.selector); - bytes memory report; - s_commitStore.report(report, ++s_latestEpochAndRound); - } - - function test_InvalidRootRevert() public { - CommitStore.CommitReport memory report = CommitStore.CommitReport({ - priceUpdates: _getEmptyPriceUpdates(), - interval: CommitStore.Interval(1, 4), - merkleRoot: bytes32(0) - }); - - vm.expectRevert(CommitStore.InvalidRoot.selector); - s_commitStore.report(abi.encode(report), ++s_latestEpochAndRound); - } - - function test_InvalidInterval_Revert() public { - CommitStore.Interval memory interval = CommitStore.Interval(2, 2); - CommitStore.CommitReport memory report = - CommitStore.CommitReport({priceUpdates: _getEmptyPriceUpdates(), interval: interval, merkleRoot: bytes32(0)}); - - vm.expectRevert(abi.encodeWithSelector(CommitStore.InvalidInterval.selector, interval)); - - s_commitStore.report(abi.encode(report), ++s_latestEpochAndRound); - } - - function test_InvalidIntervalMinLargerThanMax_Revert() public { - CommitStore.Interval memory interval = CommitStore.Interval(1, 0); - CommitStore.CommitReport memory report = - CommitStore.CommitReport({priceUpdates: _getEmptyPriceUpdates(), interval: interval, merkleRoot: bytes32(0)}); - - vm.expectRevert(abi.encodeWithSelector(CommitStore.InvalidInterval.selector, interval)); - - s_commitStore.report(abi.encode(report), ++s_latestEpochAndRound); - } - - function test_ZeroEpochAndRound_Revert() public { - CommitStore.CommitReport memory report = CommitStore.CommitReport({ - priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), - interval: CommitStore.Interval(0, 0), - merkleRoot: bytes32(0) - }); - - vm.expectRevert(CommitStore.StaleReport.selector); - - s_commitStore.report(abi.encode(report), 0); - } - - function test_OnlyPriceUpdateStaleReport_Revert() public { - CommitStore.CommitReport memory report = CommitStore.CommitReport({ - priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), - interval: CommitStore.Interval(0, 0), - merkleRoot: bytes32(0) - }); - - vm.expectEmit(); - emit FeeQuoter.UsdPerTokenUpdated(s_sourceFeeToken, 4e18, block.timestamp); - s_commitStore.report(abi.encode(report), ++s_latestEpochAndRound); - - vm.expectRevert(CommitStore.StaleReport.selector); - s_commitStore.report(abi.encode(report), s_latestEpochAndRound); - } - - function test_RootAlreadyCommitted_Revert() public { - CommitStore.CommitReport memory report = CommitStore.CommitReport({ - priceUpdates: _getEmptyPriceUpdates(), - interval: CommitStore.Interval(1, 2), - merkleRoot: "Only a single root" - }); - s_commitStore.report(abi.encode(report), ++s_latestEpochAndRound); - - report = CommitStore.CommitReport({ - priceUpdates: _getEmptyPriceUpdates(), - interval: CommitStore.Interval(3, 3), - merkleRoot: "Only a single root" - }); - - vm.expectRevert(CommitStore.RootAlreadyCommitted.selector); - - s_commitStore.report(abi.encode(report), ++s_latestEpochAndRound); - } -} - -contract CommitStore_verify is CommitStoreRealRMNSetup { - function test_NotBlessed_Success() public { - bytes32[] memory leaves = new bytes32[](1); - leaves[0] = "root"; - s_commitStore.report( - abi.encode( - CommitStore.CommitReport({ - priceUpdates: _getEmptyPriceUpdates(), - interval: CommitStore.Interval(1, 2), - merkleRoot: leaves[0] - }) - ), - ++s_latestEpochAndRound - ); - bytes32[] memory proofs = new bytes32[](0); - // We have not blessed this root, should return 0. - uint256 timestamp = s_commitStore.verify(leaves, proofs, 0); - assertEq(uint256(0), timestamp); - } - - function test_Blessed_Success() public { - bytes32[] memory leaves = new bytes32[](1); - leaves[0] = "root"; - s_commitStore.report( - abi.encode( - CommitStore.CommitReport({ - priceUpdates: _getEmptyPriceUpdates(), - interval: CommitStore.Interval(1, 2), - merkleRoot: leaves[0] - }) - ), - ++s_latestEpochAndRound - ); - // Bless that root. - IRMN.TaggedRoot[] memory taggedRoots = new IRMN.TaggedRoot[](1); - taggedRoots[0] = IRMN.TaggedRoot({commitStore: address(s_commitStore), root: leaves[0]}); - vm.startPrank(BLESS_VOTE_ADDR); - s_rmn.voteToBless(taggedRoots); - bytes32[] memory proofs = new bytes32[](0); - uint256 timestamp = s_commitStore.verify(leaves, proofs, 0); - assertEq(BLOCK_TIME, timestamp); - } - - // Reverts - - function test_Paused_Revert() public { - s_commitStore.pause(); - - bytes32[] memory hashedLeaves = new bytes32[](0); - bytes32[] memory proofs = new bytes32[](0); - uint256 proofFlagBits = 0; - - vm.expectRevert(CommitStore.PausedError.selector); - s_commitStore.verify(hashedLeaves, proofs, proofFlagBits); - } - - function test_TooManyLeaves_Revert() public { - bytes32[] memory leaves = new bytes32[](258); - bytes32[] memory proofs = new bytes32[](0); - - vm.expectRevert(MerkleMultiProof.InvalidProof.selector); - - s_commitStore.verify(leaves, proofs, 0); - } -} - -contract CommitStore_isUnpausedAndRMNHealthy is CommitStoreSetup { - function test_RMN_Success() public { - // Test pausing - assertFalse(s_commitStore.paused()); - assertTrue(s_commitStore.isUnpausedAndNotCursed()); - s_commitStore.pause(); - assertTrue(s_commitStore.paused()); - assertFalse(s_commitStore.isUnpausedAndNotCursed()); - s_commitStore.unpause(); - assertFalse(s_commitStore.paused()); - assertTrue(s_commitStore.isUnpausedAndNotCursed()); - - // Test rmn - s_mockRMN.setGlobalCursed(true); - assertFalse(s_commitStore.isUnpausedAndNotCursed()); - s_mockRMN.setGlobalCursed(false); - // TODO: also test with s_mockRMN.setChainCursed(sourceChainSelector), - // also for other similar tests (e.g., OffRamp, OnRamp) - assertTrue(s_commitStore.isUnpausedAndNotCursed()); - - s_mockRMN.setGlobalCursed(true); - s_commitStore.pause(); - assertFalse(s_commitStore.isUnpausedAndNotCursed()); - } -} - -contract CommitStore_setLatestPriceEpochAndRound is CommitStoreSetup { - function test_SetLatestPriceEpochAndRound_Success() public { - uint40 latestRoundAndEpoch = 1782155; - - vm.expectEmit(); - emit CommitStore.LatestPriceEpochAndRoundSet( - uint40(s_commitStore.getLatestPriceEpochAndRound()), latestRoundAndEpoch - ); - - s_commitStore.setLatestPriceEpochAndRound(latestRoundAndEpoch); - - assertEq(uint40(s_commitStore.getLatestPriceEpochAndRound()), latestRoundAndEpoch); - } - - // Reverts - function test_OnlyOwner_Revert() public { - vm.stopPrank(); - vm.expectRevert("Only callable by owner"); - s_commitStore.setLatestPriceEpochAndRound(6723); - } -} diff --git a/contracts/src/v0.8/ccip/test/e2e/End2End.t.sol b/contracts/src/v0.8/ccip/test/e2e/End2End.t.sol index 114265a2481..4d462adfe16 100644 --- a/contracts/src/v0.8/ccip/test/e2e/End2End.t.sol +++ b/contracts/src/v0.8/ccip/test/e2e/End2End.t.sol @@ -1,116 +1,305 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; -import "../commitStore/CommitStore.t.sol"; +import {IRMN} from "../../interfaces/IRMN.sol"; +import {IRMNRemote} from "../../interfaces/IRMNRemote.sol"; + +import {AuthorizedCallers} from "../../../shared/access/AuthorizedCallers.sol"; +import {NonceManager} from "../../NonceManager.sol"; +import {LockReleaseTokenPool} from "../../pools/LockReleaseTokenPool.sol"; +import {TokenAdminRegistry} from "../../tokenAdminRegistry/TokenAdminRegistry.sol"; import "../helpers/MerkleHelper.sol"; -import "../offRamp/EVM2EVMOffRampSetup.t.sol"; -import "../onRamp/EVM2EVMOnRampSetup.t.sol"; +import "../offRamp/OffRampSetup.t.sol"; +import "../onRamp/OnRampSetup.t.sol"; + +/// @notice This E2E test implements the following scenario: +/// 1. Send multiple messages from multiple source chains to a single destination chain (2 messages from source chain 1 and 1 from +/// source chain 2). +/// 2. Commit multiple merkle roots (1 for each source chain). +/// 3. Batch execute all the committed messages. +contract E2E is OnRampSetup, OffRampSetup { + using Internal for Internal.Any2EVMRampMessage; + + Router internal s_sourceRouter2; + OnRampHelper internal s_onRamp2; + TokenAdminRegistry internal s_tokenAdminRegistry2; + NonceManager internal s_nonceManager2; + + bytes32 internal s_metadataHash2; + + mapping(address destPool => address sourcePool) internal s_sourcePoolByDestPool; + + function setUp() public virtual override(OnRampSetup, OffRampSetup) { + OnRampSetup.setUp(); + OffRampSetup.setUp(); + + // Deploy new source router for the new source chain + s_sourceRouter2 = new Router(s_sourceRouter.getWrappedNative(), address(s_mockRMN)); -contract E2E is EVM2EVMOnRampSetup, CommitStoreSetup, EVM2EVMOffRampSetup { - using Internal for Internal.EVM2EVMMessage; + // Deploy new TokenAdminRegistry for the new source chain + s_tokenAdminRegistry2 = new TokenAdminRegistry(); - function setUp() public virtual override(EVM2EVMOnRampSetup, CommitStoreSetup, EVM2EVMOffRampSetup) { - EVM2EVMOnRampSetup.setUp(); - CommitStoreSetup.setUp(); - EVM2EVMOffRampSetup.setUp(); + // Deploy new token pools and set them on the new TokenAdminRegistry + for (uint256 i = 0; i < s_sourceTokens.length; ++i) { + address token = s_sourceTokens[i]; + address pool = address( + new LockReleaseTokenPool(IERC20(token), new address[](0), address(s_mockRMN), true, address(s_sourceRouter2)) + ); - deployOffRamp(s_commitStore, s_destRouter, address(0)); + s_sourcePoolByDestPool[s_destPoolBySourceToken[token]] = pool; + + _setPool( + s_tokenAdminRegistry2, token, pool, DEST_CHAIN_SELECTOR, s_destPoolByToken[s_destTokens[i]], s_destTokens[i] + ); + } + + for (uint256 i = 0; i < s_destTokens.length; ++i) { + address token = s_destTokens[i]; + address pool = s_destPoolByToken[token]; + + _setPool( + s_tokenAdminRegistry2, token, pool, SOURCE_CHAIN_SELECTOR + 1, s_sourcePoolByDestPool[pool], s_sourceTokens[i] + ); + } + + s_nonceManager2 = new NonceManager(new address[](0)); + + ( + // Deploy the new source chain onramp + // Outsource to shared helper function with OnRampSetup + s_onRamp2, + s_metadataHash2 + ) = _deployOnRamp( + SOURCE_CHAIN_SELECTOR + 1, s_sourceRouter2, address(s_nonceManager2), address(s_tokenAdminRegistry2) + ); + + address[] memory authorizedCallers = new address[](1); + authorizedCallers[0] = address(s_onRamp2); + s_nonceManager2.applyAuthorizedCallerUpdates( + AuthorizedCallers.AuthorizedCallerArgs({addedCallers: authorizedCallers, removedCallers: new address[](0)}) + ); + + // Enable destination chain on new source chain router + Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); + onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: address(s_onRamp2)}); + s_sourceRouter2.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), new Router.OffRamp[](0)); + + // Deploy offramp + _deployOffRamp(s_mockRMNRemote, s_inboundNonceManager); + + // Enable source chains on offramp + OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](2); + sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ + router: s_destRouter, + sourceChainSelector: SOURCE_CHAIN_SELECTOR, + isEnabled: true, + // Must match OnRamp address + onRamp: abi.encode(address(s_onRamp)) + }); + sourceChainConfigs[1] = OffRamp.SourceChainConfigArgs({ + router: s_destRouter, + sourceChainSelector: SOURCE_CHAIN_SELECTOR + 1, + isEnabled: true, + onRamp: abi.encode(address(s_onRamp2)) + }); + + _setupMultipleOffRampsFromConfigs(sourceChainConfigs); } - function test_E2E_3MessagesSuccess_gas() public { + function test_E2E_3MessagesMMultiOffRampSuccess_gas() public { vm.pauseGasMetering(); - IERC20 token0 = IERC20(s_sourceTokens[0]); - IERC20 token1 = IERC20(s_sourceTokens[1]); - uint256 balance0Pre = token0.balanceOf(OWNER); - uint256 balance1Pre = token1.balanceOf(OWNER); - - Internal.EVM2EVMMessage[] memory messages = new Internal.EVM2EVMMessage[](3); - messages[0] = sendRequest(1); - messages[1] = sendRequest(2); - messages[2] = sendRequest(3); - - uint256 expectedFee = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, _generateTokenMessage()); - // Asserts that the tokens have been sent and the fee has been paid. - assertEq(balance0Pre - messages.length * (i_tokenAmount0 + expectedFee), token0.balanceOf(OWNER)); - assertEq(balance1Pre - messages.length * i_tokenAmount1, token1.balanceOf(OWNER)); - - bytes32 metaDataHash = s_offRamp.metadataHash(); - - bytes32[] memory hashedMessages = new bytes32[](3); - hashedMessages[0] = messages[0]._hash(metaDataHash); - messages[0].messageId = hashedMessages[0]; - hashedMessages[1] = messages[1]._hash(metaDataHash); - messages[1].messageId = hashedMessages[1]; - hashedMessages[2] = messages[2]._hash(metaDataHash); - messages[2].messageId = hashedMessages[2]; - - bytes32[] memory merkleRoots = new bytes32[](1); - merkleRoots[0] = MerkleHelper.getMerkleRoot(hashedMessages); - - address[] memory onRamps = new address[](1); - onRamps[0] = ON_RAMP_ADDRESS; - - bytes memory commitReport = abi.encode( - CommitStore.CommitReport({ - priceUpdates: _getEmptyPriceUpdates(), - interval: CommitStore.Interval(messages[0].sequenceNumber, messages[2].sequenceNumber), + + Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); + Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); + + // Scoped to sending to reduce stack pressure + { + IERC20 token0 = IERC20(s_sourceTokens[0]); + IERC20 token1 = IERC20(s_sourceTokens[1]); + + uint256 balance0Pre = token0.balanceOf(OWNER); + uint256 balance1Pre = token1.balanceOf(OWNER); + + // Send messages + messages1[0] = _sendRequest(1, SOURCE_CHAIN_SELECTOR, 1, s_metadataHash, s_sourceRouter, s_tokenAdminRegistry); + messages1[1] = _sendRequest(2, SOURCE_CHAIN_SELECTOR, 2, s_metadataHash, s_sourceRouter, s_tokenAdminRegistry); + messages2[0] = + _sendRequest(1, SOURCE_CHAIN_SELECTOR + 1, 1, s_metadataHash2, s_sourceRouter2, s_tokenAdminRegistry2); + + uint256 expectedFee = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, _generateTokenMessage()); + // Asserts that the tokens have been sent and the fee has been paid. + assertEq( + balance0Pre - (messages1.length + messages2.length) * (i_tokenAmount0 + expectedFee), token0.balanceOf(OWNER) + ); + assertEq(balance1Pre - (messages1.length + messages2.length) * i_tokenAmount1, token1.balanceOf(OWNER)); + } + + // Commit + + bytes32[] memory merkleRoots = new bytes32[](2); + + // Scoped to commit to reduce stack pressure + { + bytes32[] memory hashedMessages1 = new bytes32[](2); + hashedMessages1[0] = _hashMessage(messages1[0], abi.encode(address(s_onRamp))); + hashedMessages1[1] = _hashMessage(messages1[1], abi.encode(address(s_onRamp))); + bytes32[] memory hashedMessages2 = new bytes32[](1); + hashedMessages2[0] = _hashMessage(messages2[0], abi.encode(address(s_onRamp2))); + + merkleRoots[0] = MerkleHelper.getMerkleRoot(hashedMessages1); + merkleRoots[1] = MerkleHelper.getMerkleRoot(hashedMessages2); + + // TODO make these real sigs :) + IRMNRemote.Signature[] memory rmnSignatures = new IRMNRemote.Signature[](0); + + Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](2); + roots[0] = Internal.MerkleRoot({ + sourceChainSelector: SOURCE_CHAIN_SELECTOR, + onRampAddress: abi.encode(address(s_onRamp)), + minSeqNr: messages1[0].header.sequenceNumber, + maxSeqNr: messages1[1].header.sequenceNumber, merkleRoot: merkleRoots[0] - }) - ); + }); + roots[1] = Internal.MerkleRoot({ + sourceChainSelector: SOURCE_CHAIN_SELECTOR + 1, + onRampAddress: abi.encode(address(s_onRamp2)), + minSeqNr: messages2[0].header.sequenceNumber, + maxSeqNr: messages2[0].header.sequenceNumber, + merkleRoot: merkleRoots[1] + }); - vm.resumeGasMetering(); - s_commitStore.report(commitReport, ++s_latestEpochAndRound); - vm.pauseGasMetering(); + OffRamp.CommitReport memory report = OffRamp.CommitReport({ + priceUpdates: _getEmptyPriceUpdates(), + merkleRoots: roots, + rmnSignatures: rmnSignatures, + rmnRawVs: 0 + }); - s_mockRMN.setTaggedRootBlessed(IRMN.TaggedRoot({commitStore: address(s_commitStore), root: merkleRoots[0]}), true); + vm.resumeGasMetering(); + _commit(report, ++s_latestSequenceNumber); + vm.pauseGasMetering(); + } - bytes32[] memory proofs = new bytes32[](0); - uint256 timestamp = s_commitStore.verify(merkleRoots, proofs, 2 ** 2 - 1); - assertEq(BLOCK_TIME, timestamp); + // Scoped to RMN and verify to reduce stack pressure + { + s_mockRMN.setTaggedRootBlessed(IRMN.TaggedRoot({commitStore: address(s_offRamp), root: merkleRoots[0]}), true); + s_mockRMN.setTaggedRootBlessed(IRMN.TaggedRoot({commitStore: address(s_offRamp), root: merkleRoots[1]}), true); - // We change the block time so when execute would e.g. use the current - // block time instead of the committed block time the value would be - // incorrect in the checks below. - vm.warp(BLOCK_TIME + 2000); + bytes32[] memory proofs = new bytes32[](0); + bytes32[] memory hashedLeaves = new bytes32[](1); + hashedLeaves[0] = merkleRoots[0]; - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); + uint256 timestamp = s_offRamp.verify(SOURCE_CHAIN_SELECTOR, hashedLeaves, proofs, 2 ** 2 - 1); + assertEq(BLOCK_TIME, timestamp); + hashedLeaves[0] = merkleRoots[1]; + timestamp = s_offRamp.verify(SOURCE_CHAIN_SELECTOR + 1, hashedLeaves, proofs, 2 ** 2 - 1); + assertEq(BLOCK_TIME, timestamp); - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[1].sequenceNumber, messages[1].messageId, Internal.MessageExecutionState.SUCCESS, "" + // We change the block time so when execute would e.g. use the current + // block time instead of the committed block time the value would be + // incorrect in the checks below. + vm.warp(BLOCK_TIME + 2000); + } + + // Execute + + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); + reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR, messages1); + reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR + 1, messages2); + + vm.resumeGasMetering(); + vm.recordLogs(); + _execute(reports); + + assertExecutionStateChangedEventLogs( + SOURCE_CHAIN_SELECTOR, + messages1[0].header.sequenceNumber, + messages1[0].header.messageId, + _hashMessage(messages1[0], abi.encode(address(s_onRamp))), + Internal.MessageExecutionState.SUCCESS, + "" ); - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[2].sequenceNumber, messages[2].messageId, Internal.MessageExecutionState.SUCCESS, "" + assertExecutionStateChangedEventLogs( + SOURCE_CHAIN_SELECTOR, + messages1[1].header.sequenceNumber, + messages1[1].header.messageId, + _hashMessage(messages1[1], abi.encode(address(s_onRamp))), + Internal.MessageExecutionState.SUCCESS, + "" ); - Internal.ExecutionReport memory execReport = _generateReportFromMessages(messages); - vm.resumeGasMetering(); - s_offRamp.execute(execReport, new EVM2EVMOffRamp.GasLimitOverride[](0)); + assertExecutionStateChangedEventLogs( + SOURCE_CHAIN_SELECTOR + 1, + messages2[0].header.sequenceNumber, + messages2[0].header.messageId, + _hashMessage(messages2[0], abi.encode(address(s_onRamp2))), + Internal.MessageExecutionState.SUCCESS, + "" + ); } - function sendRequest(uint64 expectedSeqNum) public returns (Internal.EVM2EVMMessage memory) { + function _sendRequest( + uint64 expectedSeqNum, + uint64 sourceChainSelector, + uint64 nonce, + bytes32 metadataHash, + Router router, + TokenAdminRegistry tokenAdminRegistry + ) public returns (Internal.Any2EVMRampMessage memory) { Client.EVM2AnyMessage memory message = _generateTokenMessage(); - uint256 expectedFee = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, message); + IERC20(s_sourceTokens[0]).approve(address(router), i_tokenAmount0 + router.getFee(DEST_CHAIN_SELECTOR, message)); + IERC20(s_sourceTokens[1]).approve(address(router), i_tokenAmount1); - IERC20(s_sourceTokens[0]).approve(address(s_sourceRouter), i_tokenAmount0 + expectedFee); - IERC20(s_sourceTokens[1]).approve(address(s_sourceRouter), i_tokenAmount1); + uint256 feeAmount = router.getFee(DEST_CHAIN_SELECTOR, message); message.receiver = abi.encode(address(s_receiver)); - Internal.EVM2EVMMessage memory msgEvent = - _messageToEvent(message, expectedSeqNum, expectedSeqNum, expectedFee, OWNER); + Internal.EVM2AnyRampMessage memory msgEvent = _messageToEvent( + message, + sourceChainSelector, + DEST_CHAIN_SELECTOR, + expectedSeqNum, + nonce, + feeAmount, + feeAmount, + OWNER, + metadataHash, + tokenAdminRegistry + ); vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(msgEvent); + emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, expectedSeqNum, msgEvent); vm.resumeGasMetering(); - s_sourceRouter.ccipSend(DEST_CHAIN_SELECTOR, message); + router.ccipSend(DEST_CHAIN_SELECTOR, message); vm.pauseGasMetering(); - return msgEvent; + Internal.Any2EVMTokenTransfer[] memory any2EVMTokenTransfer = + new Internal.Any2EVMTokenTransfer[](message.tokenAmounts.length); + + for (uint256 i = 0; i < msgEvent.tokenAmounts.length; ++i) { + any2EVMTokenTransfer[i] = Internal.Any2EVMTokenTransfer({ + sourcePoolAddress: abi.encode(msgEvent.tokenAmounts[i].sourcePoolAddress), + destTokenAddress: abi.decode(msgEvent.tokenAmounts[i].destTokenAddress, (address)), + extraData: msgEvent.tokenAmounts[i].extraData, + amount: msgEvent.tokenAmounts[i].amount, + destGasAmount: abi.decode(msgEvent.tokenAmounts[i].destExecData, (uint32)) + }); + } + + return Internal.Any2EVMRampMessage({ + header: Internal.RampMessageHeader({ + messageId: msgEvent.header.messageId, + sourceChainSelector: sourceChainSelector, + destChainSelector: DEST_CHAIN_SELECTOR, + sequenceNumber: msgEvent.header.sequenceNumber, + nonce: msgEvent.header.nonce + }), + sender: abi.encode(msgEvent.sender), + data: msgEvent.data, + receiver: abi.decode(msgEvent.receiver, (address)), + gasLimit: s_feeQuoter.parseEVMExtraArgsFromBytes(msgEvent.extraArgs, DEST_CHAIN_SELECTOR).gasLimit, + tokenAmounts: any2EVMTokenTransfer + }); } } diff --git a/contracts/src/v0.8/ccip/test/e2e/MultiRampsEnd2End.t.sol b/contracts/src/v0.8/ccip/test/e2e/MultiRampsEnd2End.t.sol deleted file mode 100644 index 48bfa668df0..00000000000 --- a/contracts/src/v0.8/ccip/test/e2e/MultiRampsEnd2End.t.sol +++ /dev/null @@ -1,305 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {IRMN} from "../../interfaces/IRMN.sol"; - -import {AuthorizedCallers} from "../../../shared/access/AuthorizedCallers.sol"; -import {NonceManager} from "../../NonceManager.sol"; -import {IRMNRemote} from "../../interfaces/IRMNRemote.sol"; -import {LockReleaseTokenPool} from "../../pools/LockReleaseTokenPool.sol"; -import {TokenAdminRegistry} from "../../tokenAdminRegistry/TokenAdminRegistry.sol"; -import "../helpers/MerkleHelper.sol"; -import "../offRamp/OffRampSetup.t.sol"; -import "../onRamp/OnRampSetup.t.sol"; - -/// @notice This E2E test implements the following scenario: -/// 1. Send multiple messages from multiple source chains to a single destination chain (2 messages from source chain 1 and 1 from -/// source chain 2). -/// 2. Commit multiple merkle roots (1 for each source chain). -/// 3. Batch execute all the committed messages. -contract MultiRampsE2E is OnRampSetup, OffRampSetup { - using Internal for Internal.Any2EVMRampMessage; - - Router internal s_sourceRouter2; - OnRampHelper internal s_onRamp2; - TokenAdminRegistry internal s_tokenAdminRegistry2; - NonceManager internal s_nonceManager2; - - bytes32 internal s_metadataHash2; - - mapping(address destPool => address sourcePool) internal s_sourcePoolByDestPool; - - function setUp() public virtual override(OnRampSetup, OffRampSetup) { - OnRampSetup.setUp(); - OffRampSetup.setUp(); - - // Deploy new source router for the new source chain - s_sourceRouter2 = new Router(s_sourceRouter.getWrappedNative(), address(s_mockRMN)); - - // Deploy new TokenAdminRegistry for the new source chain - s_tokenAdminRegistry2 = new TokenAdminRegistry(); - - // Deploy new token pools and set them on the new TokenAdminRegistry - for (uint256 i = 0; i < s_sourceTokens.length; ++i) { - address token = s_sourceTokens[i]; - address pool = address( - new LockReleaseTokenPool(IERC20(token), new address[](0), address(s_mockRMN), true, address(s_sourceRouter2)) - ); - - s_sourcePoolByDestPool[s_destPoolBySourceToken[token]] = pool; - - _setPool( - s_tokenAdminRegistry2, token, pool, DEST_CHAIN_SELECTOR, s_destPoolByToken[s_destTokens[i]], s_destTokens[i] - ); - } - - for (uint256 i = 0; i < s_destTokens.length; ++i) { - address token = s_destTokens[i]; - address pool = s_destPoolByToken[token]; - - _setPool( - s_tokenAdminRegistry2, token, pool, SOURCE_CHAIN_SELECTOR + 1, s_sourcePoolByDestPool[pool], s_sourceTokens[i] - ); - } - - s_nonceManager2 = new NonceManager(new address[](0)); - - ( - // Deploy the new source chain onramp - // Outsource to shared helper function with OnRampSetup - s_onRamp2, - s_metadataHash2 - ) = _deployOnRamp( - SOURCE_CHAIN_SELECTOR + 1, s_sourceRouter2, address(s_nonceManager2), address(s_tokenAdminRegistry2) - ); - - address[] memory authorizedCallers = new address[](1); - authorizedCallers[0] = address(s_onRamp2); - s_nonceManager2.applyAuthorizedCallerUpdates( - AuthorizedCallers.AuthorizedCallerArgs({addedCallers: authorizedCallers, removedCallers: new address[](0)}) - ); - - // Enable destination chain on new source chain router - Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); - onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: address(s_onRamp2)}); - s_sourceRouter2.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), new Router.OffRamp[](0)); - - // Deploy offramp - _deployOffRamp(s_mockRMNRemote, s_inboundNonceManager); - - // Enable source chains on offramp - OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](2); - sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ - router: s_destRouter, - sourceChainSelector: SOURCE_CHAIN_SELECTOR, - isEnabled: true, - // Must match OnRamp address - onRamp: abi.encode(address(s_onRamp)) - }); - sourceChainConfigs[1] = OffRamp.SourceChainConfigArgs({ - router: s_destRouter, - sourceChainSelector: SOURCE_CHAIN_SELECTOR + 1, - isEnabled: true, - onRamp: abi.encode(address(s_onRamp2)) - }); - - _setupMultipleOffRampsFromConfigs(sourceChainConfigs); - } - - function test_E2E_3MessagesMMultiOffRampSuccess_gas() public { - vm.pauseGasMetering(); - - Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); - Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); - - // Scoped to sending to reduce stack pressure - { - IERC20 token0 = IERC20(s_sourceTokens[0]); - IERC20 token1 = IERC20(s_sourceTokens[1]); - - uint256 balance0Pre = token0.balanceOf(OWNER); - uint256 balance1Pre = token1.balanceOf(OWNER); - - // Send messages - messages1[0] = _sendRequest(1, SOURCE_CHAIN_SELECTOR, 1, s_metadataHash, s_sourceRouter, s_tokenAdminRegistry); - messages1[1] = _sendRequest(2, SOURCE_CHAIN_SELECTOR, 2, s_metadataHash, s_sourceRouter, s_tokenAdminRegistry); - messages2[0] = - _sendRequest(1, SOURCE_CHAIN_SELECTOR + 1, 1, s_metadataHash2, s_sourceRouter2, s_tokenAdminRegistry2); - - uint256 expectedFee = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, _generateTokenMessage()); - // Asserts that the tokens have been sent and the fee has been paid. - assertEq( - balance0Pre - (messages1.length + messages2.length) * (i_tokenAmount0 + expectedFee), token0.balanceOf(OWNER) - ); - assertEq(balance1Pre - (messages1.length + messages2.length) * i_tokenAmount1, token1.balanceOf(OWNER)); - } - - // Commit - - bytes32[] memory merkleRoots = new bytes32[](2); - - // Scoped to commit to reduce stack pressure - { - bytes32[] memory hashedMessages1 = new bytes32[](2); - hashedMessages1[0] = _hashMessage(messages1[0], abi.encode(address(s_onRamp))); - hashedMessages1[1] = _hashMessage(messages1[1], abi.encode(address(s_onRamp))); - bytes32[] memory hashedMessages2 = new bytes32[](1); - hashedMessages2[0] = _hashMessage(messages2[0], abi.encode(address(s_onRamp2))); - - merkleRoots[0] = MerkleHelper.getMerkleRoot(hashedMessages1); - merkleRoots[1] = MerkleHelper.getMerkleRoot(hashedMessages2); - - // TODO make these real sigs :) - IRMNRemote.Signature[] memory rmnSignatures = new IRMNRemote.Signature[](0); - - Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](2); - roots[0] = Internal.MerkleRoot({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR, - onRampAddress: abi.encode(address(s_onRamp)), - minSeqNr: messages1[0].header.sequenceNumber, - maxSeqNr: messages1[1].header.sequenceNumber, - merkleRoot: merkleRoots[0] - }); - roots[1] = Internal.MerkleRoot({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR + 1, - onRampAddress: abi.encode(address(s_onRamp2)), - minSeqNr: messages2[0].header.sequenceNumber, - maxSeqNr: messages2[0].header.sequenceNumber, - merkleRoot: merkleRoots[1] - }); - - OffRamp.CommitReport memory report = OffRamp.CommitReport({ - priceUpdates: _getEmptyPriceUpdates(), - merkleRoots: roots, - rmnSignatures: rmnSignatures, - rmnRawVs: 0 - }); - - vm.resumeGasMetering(); - _commit(report, ++s_latestSequenceNumber); - vm.pauseGasMetering(); - } - - // Scoped to RMN and verify to reduce stack pressure - { - s_mockRMN.setTaggedRootBlessed(IRMN.TaggedRoot({commitStore: address(s_offRamp), root: merkleRoots[0]}), true); - s_mockRMN.setTaggedRootBlessed(IRMN.TaggedRoot({commitStore: address(s_offRamp), root: merkleRoots[1]}), true); - - bytes32[] memory proofs = new bytes32[](0); - bytes32[] memory hashedLeaves = new bytes32[](1); - hashedLeaves[0] = merkleRoots[0]; - - uint256 timestamp = s_offRamp.verify(SOURCE_CHAIN_SELECTOR, hashedLeaves, proofs, 2 ** 2 - 1); - assertEq(BLOCK_TIME, timestamp); - hashedLeaves[0] = merkleRoots[1]; - timestamp = s_offRamp.verify(SOURCE_CHAIN_SELECTOR + 1, hashedLeaves, proofs, 2 ** 2 - 1); - assertEq(BLOCK_TIME, timestamp); - - // We change the block time so when execute would e.g. use the current - // block time instead of the committed block time the value would be - // incorrect in the checks below. - vm.warp(BLOCK_TIME + 2000); - } - - // Execute - - Internal.ExecutionReportSingleChain[] memory reports = new Internal.ExecutionReportSingleChain[](2); - reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR, messages1); - reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR + 1, messages2); - - vm.resumeGasMetering(); - vm.recordLogs(); - _execute(reports); - - assertExecutionStateChangedEventLogs( - SOURCE_CHAIN_SELECTOR, - messages1[0].header.sequenceNumber, - messages1[0].header.messageId, - _hashMessage(messages1[0], abi.encode(address(s_onRamp))), - Internal.MessageExecutionState.SUCCESS, - "" - ); - - assertExecutionStateChangedEventLogs( - SOURCE_CHAIN_SELECTOR, - messages1[1].header.sequenceNumber, - messages1[1].header.messageId, - _hashMessage(messages1[1], abi.encode(address(s_onRamp))), - Internal.MessageExecutionState.SUCCESS, - "" - ); - - assertExecutionStateChangedEventLogs( - SOURCE_CHAIN_SELECTOR + 1, - messages2[0].header.sequenceNumber, - messages2[0].header.messageId, - _hashMessage(messages2[0], abi.encode(address(s_onRamp2))), - Internal.MessageExecutionState.SUCCESS, - "" - ); - } - - function _sendRequest( - uint64 expectedSeqNum, - uint64 sourceChainSelector, - uint64 nonce, - bytes32 metadataHash, - Router router, - TokenAdminRegistry tokenAdminRegistry - ) public returns (Internal.Any2EVMRampMessage memory) { - Client.EVM2AnyMessage memory message = _generateTokenMessage(); - IERC20(s_sourceTokens[0]).approve(address(router), i_tokenAmount0 + router.getFee(DEST_CHAIN_SELECTOR, message)); - IERC20(s_sourceTokens[1]).approve(address(router), i_tokenAmount1); - - uint256 feeAmount = router.getFee(DEST_CHAIN_SELECTOR, message); - - message.receiver = abi.encode(address(s_receiver)); - Internal.EVM2AnyRampMessage memory msgEvent = _messageToEvent( - message, - sourceChainSelector, - DEST_CHAIN_SELECTOR, - expectedSeqNum, - nonce, - feeAmount, - feeAmount, - OWNER, - metadataHash, - tokenAdminRegistry - ); - - vm.expectEmit(); - emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, expectedSeqNum, msgEvent); - - vm.resumeGasMetering(); - router.ccipSend(DEST_CHAIN_SELECTOR, message); - vm.pauseGasMetering(); - - Internal.Any2EVMTokenTransfer[] memory any2EVMTokenTransfer = - new Internal.Any2EVMTokenTransfer[](message.tokenAmounts.length); - - for (uint256 i = 0; i < msgEvent.tokenAmounts.length; ++i) { - any2EVMTokenTransfer[i] = Internal.Any2EVMTokenTransfer({ - sourcePoolAddress: abi.encode(msgEvent.tokenAmounts[i].sourcePoolAddress), - destTokenAddress: abi.decode(msgEvent.tokenAmounts[i].destTokenAddress, (address)), - extraData: msgEvent.tokenAmounts[i].extraData, - amount: msgEvent.tokenAmounts[i].amount, - destGasAmount: abi.decode(msgEvent.tokenAmounts[i].destExecData, (uint32)) - }); - } - - return Internal.Any2EVMRampMessage({ - header: Internal.RampMessageHeader({ - messageId: msgEvent.header.messageId, - sourceChainSelector: sourceChainSelector, - destChainSelector: DEST_CHAIN_SELECTOR, - sequenceNumber: msgEvent.header.sequenceNumber, - nonce: msgEvent.header.nonce - }), - sender: abi.encode(msgEvent.sender), - data: msgEvent.data, - receiver: abi.decode(msgEvent.receiver, (address)), - gasLimit: s_feeQuoter.parseEVMExtraArgsFromBytes(msgEvent.extraArgs, DEST_CHAIN_SELECTOR).gasLimit, - tokenAmounts: any2EVMTokenTransfer - }); - } -} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.t.sol index 41720898dd2..3356b257cb2 100644 --- a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.t.sol +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.t.sol @@ -857,7 +857,7 @@ contract FeeQuoter_getDataAvailabilityCost is FeeQuoterSetup { FeeQuoter.DestChainConfig memory destChainConfig = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR); uint256 dataAvailabilityGas = destChainConfig.destDataAvailabilityOverheadGas - + destChainConfig.destGasPerDataAvailabilityByte * Internal.ANY_2_EVM_MESSAGE_FIXED_BYTES; + + destChainConfig.destGasPerDataAvailabilityByte * Internal.MESSAGE_FIXED_BYTES; uint256 expectedDataAvailabilityCostUSD = USD_PER_DATA_AVAILABILITY_GAS * dataAvailabilityGas * destChainConfig.destDataAvailabilityMultiplierBps * 1e14; @@ -878,7 +878,7 @@ contract FeeQuoter_getDataAvailabilityCost is FeeQuoterSetup { uint256 dataAvailabilityCostUSD2 = s_feeQuoter.getDataAvailabilityCost(DEST_CHAIN_SELECTOR + 1, USD_PER_DATA_AVAILABILITY_GAS, 0, 0, 0); dataAvailabilityGas = destChainConfig.destDataAvailabilityOverheadGas - + destChainConfig.destGasPerDataAvailabilityByte * Internal.ANY_2_EVM_MESSAGE_FIXED_BYTES; + + destChainConfig.destGasPerDataAvailabilityByte * Internal.MESSAGE_FIXED_BYTES; expectedDataAvailabilityCostUSD = USD_PER_DATA_AVAILABILITY_GAS * dataAvailabilityGas * destChainConfig.destDataAvailabilityMultiplierBps * 1e14; @@ -893,7 +893,7 @@ contract FeeQuoter_getDataAvailabilityCost is FeeQuoterSetup { FeeQuoter.DestChainConfig memory destChainConfig = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR); uint256 dataAvailabilityLengthBytes = - Internal.ANY_2_EVM_MESSAGE_FIXED_BYTES + 100 + (5 * Internal.ANY_2_EVM_MESSAGE_FIXED_BYTES_PER_TOKEN) + 50; + Internal.MESSAGE_FIXED_BYTES + 100 + (5 * Internal.MESSAGE_FIXED_BYTES_PER_TOKEN) + 50; uint256 dataAvailabilityGas = destChainConfig.destDataAvailabilityOverheadGas + destChainConfig.destGasPerDataAvailabilityByte * dataAvailabilityLengthBytes; uint256 expectedDataAvailabilityCostUSD = @@ -952,8 +952,8 @@ contract FeeQuoter_getDataAvailabilityCost is FeeQuoterSetup { tokenTransferBytesOverhead ); - uint256 dataAvailabilityLengthBytes = Internal.ANY_2_EVM_MESSAGE_FIXED_BYTES + messageDataLength - + (numberOfTokens * Internal.ANY_2_EVM_MESSAGE_FIXED_BYTES_PER_TOKEN) + tokenTransferBytesOverhead; + uint256 dataAvailabilityLengthBytes = Internal.MESSAGE_FIXED_BYTES + messageDataLength + + (numberOfTokens * Internal.MESSAGE_FIXED_BYTES_PER_TOKEN) + tokenTransferBytesOverhead; uint256 dataAvailabilityGas = destDataAvailabilityOverheadGas + destGasPerDataAvailabilityByte * dataAvailabilityLengthBytes; diff --git a/contracts/src/v0.8/ccip/test/helpers/AggregateRateLimiterHelper.sol b/contracts/src/v0.8/ccip/test/helpers/AggregateRateLimiterHelper.sol deleted file mode 100644 index ced605a7524..00000000000 --- a/contracts/src/v0.8/ccip/test/helpers/AggregateRateLimiterHelper.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import "../../AggregateRateLimiter.sol"; - -contract AggregateRateLimiterHelper is AggregateRateLimiter { - constructor(RateLimiter.Config memory config) AggregateRateLimiter(config) {} - - function rateLimitValue(uint256 value) public { - _rateLimitValue(value); - } - - function getTokenValue( - Client.EVMTokenAmount memory tokenAmount, - IPriceRegistry priceRegistry - ) public view returns (uint256) { - return _getTokenValue(tokenAmount, priceRegistry); - } -} diff --git a/contracts/src/v0.8/ccip/test/helpers/CCIPReaderTester.sol b/contracts/src/v0.8/ccip/test/helpers/CCIPReaderTester.sol deleted file mode 100644 index 7f44a8abf76..00000000000 --- a/contracts/src/v0.8/ccip/test/helpers/CCIPReaderTester.sol +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {Internal} from "../../libraries/Internal.sol"; -import {OffRamp} from "../../offRamp/OffRamp.sol"; - -contract CCIPReaderTester { - event CCIPMessageSent(uint64 indexed destChainSelector, Internal.EVM2AnyRampMessage message); - - mapping(uint64 sourceChainSelector => OffRamp.SourceChainConfig sourceChainConfig) internal s_sourceChainConfigs; - mapping(uint64 destChainSelector => uint64 sequenceNumber) internal s_destChainSeqNrs; - mapping(uint64 sourceChainSelector => mapping(bytes sender => uint64 nonce)) internal s_senderNonce; - - /// @notice Gets the next sequence number to be used in the onRamp - /// @param destChainSelector The destination chain selector - /// @return nextSequenceNumber The next sequence number to be used - function getExpectedNextSequenceNumber(uint64 destChainSelector) external view returns (uint64) { - return s_destChainSeqNrs[destChainSelector] + 1; - } - - /// @notice Sets the sequence number in the onRamp - /// @param destChainSelector The destination chain selector - /// @param sequenceNumber The sequence number - function setDestChainSeqNr(uint64 destChainSelector, uint64 sequenceNumber) external { - s_destChainSeqNrs[destChainSelector] = sequenceNumber; - } - - /// @notice Returns the inbound nonce for a given sender on a given source chain. - /// @param sourceChainSelector The source chain selector. - /// @param sender The encoded sender address. - /// @return inboundNonce The inbound nonce. - function getInboundNonce(uint64 sourceChainSelector, bytes calldata sender) external view returns (uint64) { - return s_senderNonce[sourceChainSelector][sender]; - } - - function setInboundNonce(uint64 sourceChainSelector, uint64 testNonce, bytes calldata sender) external { - s_senderNonce[sourceChainSelector][sender] = testNonce; - } - - function getSourceChainConfig(uint64 sourceChainSelector) external view returns (OffRamp.SourceChainConfig memory) { - return s_sourceChainConfigs[sourceChainSelector]; - } - - function setSourceChainConfig( - uint64 sourceChainSelector, - OffRamp.SourceChainConfig memory sourceChainConfig - ) external { - s_sourceChainConfigs[sourceChainSelector] = sourceChainConfig; - } - - function emitCCIPMessageSent(uint64 destChainSelector, Internal.EVM2AnyRampMessage memory message) external { - emit CCIPMessageSent(destChainSelector, message); - } - - event ExecutionStateChanged( - uint64 indexed sourceChainSelector, - uint64 indexed sequenceNumber, - bytes32 indexed messageId, - Internal.MessageExecutionState state, - bytes returnData - ); - - function emitExecutionStateChanged( - uint64 sourceChainSelector, - uint64 sequenceNumber, - bytes32 messageId, - Internal.MessageExecutionState state, - bytes memory returnData - ) external { - emit ExecutionStateChanged(sourceChainSelector, sequenceNumber, messageId, state, returnData); - } - - /// @dev !! must mirror OffRamp.sol's CommitReportAccepted event !! - event CommitReportAccepted(Internal.MerkleRoot[] merkleRoots, Internal.PriceUpdates priceUpdates); - - function emitCommitReportAccepted(OffRamp.CommitReport memory report) external { - emit CommitReportAccepted(report.merkleRoots, report.priceUpdates); - } -} diff --git a/contracts/src/v0.8/ccip/test/helpers/CommitStoreHelper.sol b/contracts/src/v0.8/ccip/test/helpers/CommitStoreHelper.sol deleted file mode 100644 index c8d66b8d72f..00000000000 --- a/contracts/src/v0.8/ccip/test/helpers/CommitStoreHelper.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import "../../CommitStore.sol"; - -contract CommitStoreHelper is CommitStore { - constructor(StaticConfig memory staticConfig) CommitStore(staticConfig) {} - - /// @dev Expose _report for tests - function report(bytes calldata commitReport, uint40 epochAndRound) external { - _report(commitReport, epochAndRound); - } -} diff --git a/contracts/src/v0.8/ccip/test/helpers/EVM2EVMOffRampHelper.sol b/contracts/src/v0.8/ccip/test/helpers/EVM2EVMOffRampHelper.sol index 1b537702be1..4720c1f8242 100644 --- a/contracts/src/v0.8/ccip/test/helpers/EVM2EVMOffRampHelper.sol +++ b/contracts/src/v0.8/ccip/test/helpers/EVM2EVMOffRampHelper.sol @@ -1,63 +1,21 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; -import "../../offRamp/EVM2EVMOffRamp.sol"; -import {IgnoreContractSize} from "./IgnoreContractSize.sol"; +contract EVM2EVMOffRampHelper { + uint64 public s_nonce; + mapping(address sender => uint64 nonce) public s_nonces; -contract EVM2EVMOffRampHelper is EVM2EVMOffRamp, IgnoreContractSize { - constructor( - StaticConfig memory staticConfig, - RateLimiter.Config memory rateLimiterConfig - ) EVM2EVMOffRamp(staticConfig, rateLimiterConfig) {} - - function setExecutionStateHelper(uint64 sequenceNumber, Internal.MessageExecutionState state) public { - _setExecutionState(sequenceNumber, state); - } - - function getExecutionStateBitMap(uint64 bitmapIndex) public view returns (uint256) { - return s_executionStates[bitmapIndex]; - } - - function releaseOrMintToken( - uint256 sourceTokenAmount, - bytes calldata originalSender, - address receiver, - Internal.SourceTokenData calldata sourceTokenData, - bytes calldata offchainTokenData - ) external returns (Client.EVMTokenAmount memory) { - return _releaseOrMintToken(sourceTokenAmount, originalSender, receiver, sourceTokenData, offchainTokenData); - } - - function releaseOrMintTokens( - Client.EVMTokenAmount[] calldata sourceTokenAmounts, - bytes calldata originalSender, - address receiver, - bytes[] calldata sourceTokenData, - bytes[] calldata offchainTokenData, - uint32[] memory tokenGasOverrides - ) external returns (Client.EVMTokenAmount[] memory) { - return _releaseOrMintTokens( - sourceTokenAmounts, originalSender, receiver, sourceTokenData, offchainTokenData, tokenGasOverrides - ); - } - - function trialExecute( - Internal.EVM2EVMMessage memory message, - bytes[] memory offchainTokenData, - uint32[] memory tokenGasOverrides - ) external returns (Internal.MessageExecutionState, bytes memory) { - return _trialExecute(message, offchainTokenData, tokenGasOverrides); - } - - function report(bytes calldata executableMessages) external { - _report(executableMessages); + function execute(address[] memory senders) external { + for (uint256 i; i < senders.length; i++) { + s_nonces[senders[i]]++; + } } - function execute(Internal.ExecutionReport memory rep, GasLimitOverride[] memory gasLimitOverrides) external { - _execute(rep, gasLimitOverrides); + function metadataHash() external pure returns (bytes32) { + return 0x0; } - function metadataHash() external view returns (bytes32) { - return _metadataHash(Internal.EVM_2_EVM_MESSAGE_HASH); + function getSenderNonce(address sender) external view returns (uint64 nonce) { + return s_nonces[sender]; } } diff --git a/contracts/src/v0.8/ccip/test/helpers/EVM2EVMOnRampHelper.sol b/contracts/src/v0.8/ccip/test/helpers/EVM2EVMOnRampHelper.sol deleted file mode 100644 index 5cce6aaa445..00000000000 --- a/contracts/src/v0.8/ccip/test/helpers/EVM2EVMOnRampHelper.sol +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import "../../onRamp/EVM2EVMOnRamp.sol"; -import {IgnoreContractSize} from "./IgnoreContractSize.sol"; - -contract EVM2EVMOnRampHelper is EVM2EVMOnRamp, IgnoreContractSize { - constructor( - StaticConfig memory staticConfig, - DynamicConfig memory dynamicConfig, - RateLimiter.Config memory rateLimiterConfig, - FeeTokenConfigArgs[] memory feeTokenConfigs, - TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs, - NopAndWeight[] memory nopsAndWeights - ) - EVM2EVMOnRamp( - staticConfig, - dynamicConfig, - rateLimiterConfig, - feeTokenConfigs, - tokenTransferFeeConfigArgs, - nopsAndWeights - ) - {} - - function getDataAvailabilityCost( - uint112 dataAvailabilityGasPrice, - uint256 messageDataLength, - uint256 numberOfTokens, - uint32 tokenTransferBytesOverhead - ) external view returns (uint256) { - return - _getDataAvailabilityCost(dataAvailabilityGasPrice, messageDataLength, numberOfTokens, tokenTransferBytesOverhead); - } - - function getTokenTransferCost( - address feeToken, - uint224 feeTokenPrice, - Client.EVMTokenAmount[] calldata tokenAmounts - ) external view returns (uint256, uint32, uint32) { - return _getTokenTransferCost(feeToken, feeTokenPrice, tokenAmounts); - } - - function getSequenceNumber() external view returns (uint64) { - return s_sequenceNumber; - } -} diff --git a/contracts/src/v0.8/ccip/test/helpers/MaybeRevertingBurnMintTokenPool.sol b/contracts/src/v0.8/ccip/test/helpers/MaybeRevertingBurnMintTokenPool.sol index 7290e915099..5547bd30818 100644 --- a/contracts/src/v0.8/ccip/test/helpers/MaybeRevertingBurnMintTokenPool.sol +++ b/contracts/src/v0.8/ccip/test/helpers/MaybeRevertingBurnMintTokenPool.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; import {IBurnMintERC20} from "../../../shared/token/ERC20/IBurnMintERC20.sol"; diff --git a/contracts/src/v0.8/ccip/test/helpers/MerkleHelper.sol b/contracts/src/v0.8/ccip/test/helpers/MerkleHelper.sol index ccb05681f1c..8932888e18e 100644 --- a/contracts/src/v0.8/ccip/test/helpers/MerkleHelper.sol +++ b/contracts/src/v0.8/ccip/test/helpers/MerkleHelper.sol @@ -15,6 +15,7 @@ library MerkleHelper { /// / \ /// a b function getMerkleRoot(bytes32[] memory hashedLeaves) public pure returns (bytes32) { + // solhint-disable-next-line reason-string,gas-custom-errors require(hashedLeaves.length <= 256); while (hashedLeaves.length > 1) { hashedLeaves = computeNextLayer(hashedLeaves); diff --git a/contracts/src/v0.8/ccip/test/helpers/MessageHasher.sol b/contracts/src/v0.8/ccip/test/helpers/MessageHasher.sol index c3bd4a5e911..0c40dbe2122 100644 --- a/contracts/src/v0.8/ccip/test/helpers/MessageHasher.sol +++ b/contracts/src/v0.8/ccip/test/helpers/MessageHasher.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.4; import {Client} from "../../libraries/Client.sol"; diff --git a/contracts/src/v0.8/ccip/test/helpers/MultiTokenPool.sol b/contracts/src/v0.8/ccip/test/helpers/MultiTokenPool.sol index 5a1bf5021d9..38cf98cbf56 100644 --- a/contracts/src/v0.8/ccip/test/helpers/MultiTokenPool.sol +++ b/contracts/src/v0.8/ccip/test/helpers/MultiTokenPool.sol @@ -337,7 +337,7 @@ abstract contract MultiTokenPool is IPoolV1, OwnerIsCreator { /// @param remoteChainSelector The remote chain selector for which the rate limits apply. /// @param outboundConfig The new outbound rate limiter config, meaning the onRamp rate limits for the given chain. /// @param inboundConfig The new inbound rate limiter config, meaning the offRamp rate limits for the given chain. - function setChainRateLimiterConfig( + function _setChainRateLimiterConfig( address token, uint64 remoteChainSelector, RateLimiter.Config memory outboundConfig, diff --git a/contracts/src/v0.8/ccip/test/helpers/OCR2Helper.sol b/contracts/src/v0.8/ccip/test/helpers/OCR2Helper.sol deleted file mode 100644 index cb66352ff65..00000000000 --- a/contracts/src/v0.8/ccip/test/helpers/OCR2Helper.sol +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {OCR2Base} from "../../ocr/OCR2Base.sol"; - -contract OCR2Helper is OCR2Base(false) { - function configDigestFromConfigData( - uint256 chainSelector, - address contractAddress, - uint64 configCount, - address[] memory signers, - address[] memory transmitters, - uint8 f, - bytes memory onchainConfig, - uint64 offchainConfigVersion, - bytes memory offchainConfig - ) public pure returns (bytes32) { - return _configDigestFromConfigData( - chainSelector, - contractAddress, - configCount, - signers, - transmitters, - f, - onchainConfig, - offchainConfigVersion, - offchainConfig - ); - } - - function _report(bytes calldata report, uint40 epochAndRound) internal override {} - - function typeAndVersion() public pure override returns (string memory) { - return "OCR2BaseHelper 1.0.0"; - } - - function _beforeSetConfig(bytes memory _onchainConfig) internal override {} -} diff --git a/contracts/src/v0.8/ccip/test/helpers/OCR2NoChecksHelper.sol b/contracts/src/v0.8/ccip/test/helpers/OCR2NoChecksHelper.sol deleted file mode 100644 index a1ececa326f..00000000000 --- a/contracts/src/v0.8/ccip/test/helpers/OCR2NoChecksHelper.sol +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {OCR2BaseNoChecks} from "../../ocr/OCR2BaseNoChecks.sol"; - -contract OCR2NoChecksHelper is OCR2BaseNoChecks { - function configDigestFromConfigData( - uint256 chainSelector, - address contractAddress, - uint64 configCount, - address[] memory signers, - address[] memory transmitters, - uint8 f, - bytes memory onchainConfig, - uint64 offchainConfigVersion, - bytes memory offchainConfig - ) public pure returns (bytes32) { - return _configDigestFromConfigData( - chainSelector, - contractAddress, - configCount, - signers, - transmitters, - f, - onchainConfig, - offchainConfigVersion, - offchainConfig - ); - } - - function _report(bytes calldata report) internal override {} - - function typeAndVersion() public pure override returns (string memory) { - return "OCR2BaseHelper 1.0.0"; - } - - function _beforeSetConfig(bytes memory _onchainConfig) internal override {} -} diff --git a/contracts/src/v0.8/ccip/test/helpers/OffRampHelper.sol b/contracts/src/v0.8/ccip/test/helpers/OffRampHelper.sol index d84e23fe2fc..40f7ccb5914 100644 --- a/contracts/src/v0.8/ccip/test/helpers/OffRampHelper.sol +++ b/contracts/src/v0.8/ccip/test/helpers/OffRampHelper.sol @@ -63,14 +63,14 @@ contract OffRampHelper is OffRamp, IgnoreContractSize { } function executeSingleReport( - Internal.ExecutionReportSingleChain memory rep, + Internal.ExecutionReport memory rep, GasLimitOverride[] memory manualExecGasExecOverrides ) external { _executeSingleReport(rep, manualExecGasExecOverrides); } function batchExecute( - Internal.ExecutionReportSingleChain[] memory reports, + Internal.ExecutionReport[] memory reports, GasLimitOverride[][] memory manualExecGasLimits ) external { _batchExecute(reports, manualExecGasLimits); diff --git a/contracts/src/v0.8/ccip/test/helpers/OnRampHelper.sol b/contracts/src/v0.8/ccip/test/helpers/OnRampHelper.sol index 57397f040a5..d315a58fc5d 100644 --- a/contracts/src/v0.8/ccip/test/helpers/OnRampHelper.sol +++ b/contracts/src/v0.8/ccip/test/helpers/OnRampHelper.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; -import "../../onRamp/OnRamp.sol"; +import {OnRamp} from "../../onRamp/OnRamp.sol"; import {IgnoreContractSize} from "./IgnoreContractSize.sol"; contract OnRampHelper is OnRamp, IgnoreContractSize { diff --git a/contracts/src/v0.8/ccip/test/helpers/ReportCodec.sol b/contracts/src/v0.8/ccip/test/helpers/ReportCodec.sol index 2e50124ab4c..57a4b3a3dac 100644 --- a/contracts/src/v0.8/ccip/test/helpers/ReportCodec.sol +++ b/contracts/src/v0.8/ccip/test/helpers/ReportCodec.sol @@ -1,15 +1,15 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; import {Internal} from "../../libraries/Internal.sol"; import {OffRamp} from "../../offRamp/OffRamp.sol"; contract ReportCodec { - event ExecuteReportDecoded(Internal.ExecutionReportSingleChain[] report); + event ExecuteReportDecoded(Internal.ExecutionReport[] report); event CommitReportDecoded(OffRamp.CommitReport report); - function decodeExecuteReport(bytes memory report) public pure returns (Internal.ExecutionReportSingleChain[] memory) { - return abi.decode(report, (Internal.ExecutionReportSingleChain[])); + function decodeExecuteReport(bytes memory report) public pure returns (Internal.ExecutionReport[] memory) { + return abi.decode(report, (Internal.ExecutionReport[])); } function decodeCommitReport(bytes memory report) public pure returns (OffRamp.CommitReport memory) { diff --git a/contracts/src/v0.8/ccip/test/helpers/receivers/MaybeRevertMessageReceiver.sol b/contracts/src/v0.8/ccip/test/helpers/receivers/MaybeRevertMessageReceiver.sol index 01b9b77d376..7202ffd1d50 100644 --- a/contracts/src/v0.8/ccip/test/helpers/receivers/MaybeRevertMessageReceiver.sol +++ b/contracts/src/v0.8/ccip/test/helpers/receivers/MaybeRevertMessageReceiver.sol @@ -44,6 +44,7 @@ contract MaybeRevertMessageReceiver is IAny2EVMMessageReceiver, IERC165 { emit MessageReceived(); } + // solhint-disable-next-line no-complex-fallback receive() external payable { if (s_toRevert) { revert ReceiveRevert(); diff --git a/contracts/src/v0.8/ccip/test/helpers/receivers/MaybeRevertMessageReceiverNo165.sol b/contracts/src/v0.8/ccip/test/helpers/receivers/MaybeRevertMessageReceiverNo165.sol index 4f56394c4e5..d7a4f85ec8d 100644 --- a/contracts/src/v0.8/ccip/test/helpers/receivers/MaybeRevertMessageReceiverNo165.sol +++ b/contracts/src/v0.8/ccip/test/helpers/receivers/MaybeRevertMessageReceiverNo165.sol @@ -1,7 +1,9 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; -import "../../../interfaces/IAny2EVMMessageReceiver.sol"; +import {IAny2EVMMessageReceiver} from "../../../interfaces/IAny2EVMMessageReceiver.sol"; + +import {Client} from "../../../libraries/Client.sol"; contract MaybeRevertMessageReceiverNo165 is IAny2EVMMessageReceiver { address private s_manager; @@ -20,6 +22,7 @@ contract MaybeRevertMessageReceiverNo165 is IAny2EVMMessageReceiver { function ccipReceive(Client.Any2EVMMessage calldata) external override { if (s_toRevert) { + // solhint-disable-next-line reason-string,gas-custom-errors revert(); } emit MessageReceived(); diff --git a/contracts/src/v0.8/ccip/test/helpers/receivers/ReentrancyAbuser.sol b/contracts/src/v0.8/ccip/test/helpers/receivers/ReentrancyAbuser.sol index b69bbcaa438..5eca771a02d 100644 --- a/contracts/src/v0.8/ccip/test/helpers/receivers/ReentrancyAbuser.sol +++ b/contracts/src/v0.8/ccip/test/helpers/receivers/ReentrancyAbuser.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.24; import {CCIPReceiver} from "../../../applications/CCIPReceiver.sol"; import {Client} from "../../../libraries/Client.sol"; import {Internal} from "../../../libraries/Internal.sol"; -import {EVM2EVMOffRamp} from "../../../offRamp/EVM2EVMOffRamp.sol"; +import {OffRamp} from "../../../offRamp/OffRamp.sol"; contract ReentrancyAbuser is CCIPReceiver { event ReentrancySucceeded(); @@ -13,9 +13,9 @@ contract ReentrancyAbuser is CCIPReceiver { bool internal s_ReentrancyDone = false; Internal.ExecutionReport internal s_payload; - EVM2EVMOffRamp internal s_offRamp; + OffRamp internal s_offRamp; - constructor(address router, EVM2EVMOffRamp offRamp) CCIPReceiver(router) { + constructor(address router, OffRamp offRamp) CCIPReceiver(router) { s_offRamp = offRamp; } @@ -25,21 +25,25 @@ contract ReentrancyAbuser is CCIPReceiver { function _ccipReceive(Client.Any2EVMMessage memory) internal override { // Use original message gas limits in manual execution - EVM2EVMOffRamp.GasLimitOverride[] memory gasOverrides = _getGasLimitsFromMessages(s_payload.messages); + OffRamp.GasLimitOverride[][] memory gasOverrides = _getGasLimitsFromMessages(s_payload.messages); if (!s_ReentrancyDone) { // Could do more rounds but a PoC one is enough s_ReentrancyDone = true; - s_offRamp.manuallyExecute(s_payload, gasOverrides); + + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](1); + reports[0] = s_payload; + + s_offRamp.manuallyExecute(reports, gasOverrides); } else { emit ReentrancySucceeded(); } } function _getGasLimitsFromMessages( - Internal.EVM2EVMMessage[] memory messages - ) internal pure returns (EVM2EVMOffRamp.GasLimitOverride[] memory) { - EVM2EVMOffRamp.GasLimitOverride[] memory gasLimitOverrides = new EVM2EVMOffRamp.GasLimitOverride[](messages.length); + Internal.Any2EVMRampMessage[] memory messages + ) internal pure returns (OffRamp.GasLimitOverride[][] memory) { + OffRamp.GasLimitOverride[] memory gasLimitOverrides = new OffRamp.GasLimitOverride[](messages.length); for (uint256 i = 0; i < messages.length; ++i) { gasLimitOverrides[i].receiverExecutionGasLimit = messages[i].gasLimit; gasLimitOverrides[i].tokenGasOverrides = new uint32[](messages[i].tokenAmounts.length); @@ -49,6 +53,8 @@ contract ReentrancyAbuser is CCIPReceiver { } } - return gasLimitOverrides; + OffRamp.GasLimitOverride[][] memory gasOverrides = new OffRamp.GasLimitOverride[][](1); + gasOverrides[0] = gasLimitOverrides; + return gasOverrides; } } diff --git a/contracts/src/v0.8/ccip/test/helpers/receivers/ReentrancyAbuserMultiRamp.sol b/contracts/src/v0.8/ccip/test/helpers/receivers/ReentrancyAbuserMultiRamp.sol index c8eee488084..dbcbb24986b 100644 --- a/contracts/src/v0.8/ccip/test/helpers/receivers/ReentrancyAbuserMultiRamp.sol +++ b/contracts/src/v0.8/ccip/test/helpers/receivers/ReentrancyAbuserMultiRamp.sol @@ -10,14 +10,14 @@ contract ReentrancyAbuserMultiRamp is CCIPReceiver { event ReentrancySucceeded(); bool internal s_ReentrancyDone = false; - Internal.ExecutionReportSingleChain internal s_payload; + Internal.ExecutionReport internal s_payload; OffRamp internal s_offRamp; constructor(address router, OffRamp offRamp) CCIPReceiver(router) { s_offRamp = offRamp; } - function setPayload(Internal.ExecutionReportSingleChain calldata payload) public { + function setPayload(Internal.ExecutionReport calldata payload) public { s_payload = payload; } @@ -31,7 +31,7 @@ contract ReentrancyAbuserMultiRamp is CCIPReceiver { gasOverrides[0][i].tokenGasOverrides = new uint32[](s_payload.messages[i].tokenAmounts.length); } - Internal.ExecutionReportSingleChain[] memory batchPayload = new Internal.ExecutionReportSingleChain[](1); + Internal.ExecutionReport[] memory batchPayload = new Internal.ExecutionReport[](1); batchPayload[0] = s_payload; if (!s_ReentrancyDone) { diff --git a/contracts/src/v0.8/ccip/test/legacy/BurnMintTokenPool1_2.sol b/contracts/src/v0.8/ccip/test/legacy/BurnMintTokenPool1_2.sol deleted file mode 100644 index 44de2787c67..00000000000 --- a/contracts/src/v0.8/ccip/test/legacy/BurnMintTokenPool1_2.sol +++ /dev/null @@ -1,353 +0,0 @@ -// SPDX-License-Identifier: BUSL -pragma solidity ^0.8.20; - -import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol"; -import {IPoolPriorTo1_5} from "../../interfaces/IPoolPriorTo1_5.sol"; -import {IRMN} from "../../interfaces/IRMN.sol"; - -import {OwnerIsCreator} from "../../../shared/access/OwnerIsCreator.sol"; -import {IBurnMintERC20} from "../../../shared/token/ERC20/IBurnMintERC20.sol"; -import {RateLimiter} from "../../libraries/RateLimiter.sol"; - -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {IERC165} from "../../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/introspection/IERC165.sol"; -import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/structs/EnumerableSet.sol"; - -/// @notice Base abstract class with common functions for all token pools. -/// A token pool serves as isolated place for holding tokens and token specific logic -/// that may execute as tokens move across the bridge. -abstract contract TokenPool1_2 is IPoolPriorTo1_5, OwnerIsCreator, IERC165 { - using EnumerableSet for EnumerableSet.AddressSet; - using RateLimiter for RateLimiter.TokenBucket; - - error PermissionsError(); - error ZeroAddressNotAllowed(); - error SenderNotAllowed(address sender); - error AllowListNotEnabled(); - error NonExistentRamp(address ramp); - error BadARMSignal(); - error RampAlreadyExists(address ramp); - - event Locked(address indexed sender, uint256 amount); - event Burned(address indexed sender, uint256 amount); - event Released(address indexed sender, address indexed recipient, uint256 amount); - event Minted(address indexed sender, address indexed recipient, uint256 amount); - event OnRampAdded(address onRamp, RateLimiter.Config rateLimiterConfig); - event OnRampConfigured(address onRamp, RateLimiter.Config rateLimiterConfig); - event OnRampRemoved(address onRamp); - event OffRampAdded(address offRamp, RateLimiter.Config rateLimiterConfig); - event OffRampConfigured(address offRamp, RateLimiter.Config rateLimiterConfig); - event OffRampRemoved(address offRamp); - event AllowListAdd(address sender); - event AllowListRemove(address sender); - - struct RampUpdate { - address ramp; - bool allowed; - RateLimiter.Config rateLimiterConfig; - } - - /// @dev The bridgeable token that is managed by this pool. - IERC20 internal immutable i_token; - /// @dev The address of the arm proxy - address internal immutable i_armProxy; - /// @dev The immutable flag that indicates if the pool is access-controlled. - bool internal immutable i_allowlistEnabled; - /// @dev A set of addresses allowed to trigger lockOrBurn as original senders. - /// Only takes effect if i_allowlistEnabled is true. - /// This can be used to ensure only token-issuer specified addresses can - /// move tokens. - EnumerableSet.AddressSet internal s_allowList; - - /// @dev A set of allowed onRamps. We want the whitelist to be enumerable to - /// be able to quickly determine (without parsing logs) who can access the pool. - EnumerableSet.AddressSet internal s_onRamps; - /// @dev Inbound rate limits. This allows per destination chain - /// token issuer specified rate limiting (e.g. issuers may trust chains to varying - /// degrees and prefer different limits) - mapping(address => RateLimiter.TokenBucket) internal s_onRampRateLimits; - /// @dev A set of allowed offRamps. - EnumerableSet.AddressSet internal s_offRamps; - /// @dev Outbound rate limits. Corresponds to the inbound rate limit for the pool - /// on the remote chain. - mapping(address => RateLimiter.TokenBucket) internal s_offRampRateLimits; - - constructor(IERC20 token, address[] memory allowlist, address armProxy) { - if (address(token) == address(0)) revert ZeroAddressNotAllowed(); - i_token = token; - i_armProxy = armProxy; - - // Pool can be set as permissioned or permissionless at deployment time only to save hot-path gas. - i_allowlistEnabled = allowlist.length > 0; - if (i_allowlistEnabled) { - _applyAllowListUpdates(new address[](0), allowlist); - } - } - - /// @notice Get ARM proxy address - /// @return armProxy Address of arm proxy - function getArmProxy() public view returns (address armProxy) { - return i_armProxy; - } - - /// @inheritdoc IPoolPriorTo1_5 - function getToken() public view override returns (IERC20 token) { - return i_token; - } - - /// @inheritdoc IERC165 - function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) { - return interfaceId == type(IPoolPriorTo1_5).interfaceId || interfaceId == type(IERC165).interfaceId; - } - - // ================================================================ - // │ Ramp permissions │ - // ================================================================ - - /// @notice Checks whether something is a permissioned onRamp on this contract. - /// @return true if the given address is a permissioned onRamp. - function isOnRamp(address onRamp) public view returns (bool) { - return s_onRamps.contains(onRamp); - } - - /// @notice Checks whether something is a permissioned offRamp on this contract. - /// @return true if the given address is a permissioned offRamp. - function isOffRamp(address offRamp) public view returns (bool) { - return s_offRamps.contains(offRamp); - } - - /// @notice Get onRamp whitelist - /// @return list of onRamps. - function getOnRamps() public view returns (address[] memory) { - return s_onRamps.values(); - } - - /// @notice Get offRamp whitelist - /// @return list of offramps - function getOffRamps() public view returns (address[] memory) { - return s_offRamps.values(); - } - - /// @notice Sets permissions for all on and offRamps. - /// @dev Only callable by the owner - /// @param onRamps A list of onRamps and their new permission status/rate limits - /// @param offRamps A list of offRamps and their new permission status/rate limits - function applyRampUpdates(RampUpdate[] calldata onRamps, RampUpdate[] calldata offRamps) external virtual onlyOwner { - _applyRampUpdates(onRamps, offRamps); - } - - function _applyRampUpdates(RampUpdate[] calldata onRamps, RampUpdate[] calldata offRamps) internal onlyOwner { - for (uint256 i = 0; i < onRamps.length; ++i) { - RampUpdate memory update = onRamps[i]; - if (update.allowed) { - if (s_onRamps.add(update.ramp)) { - s_onRampRateLimits[update.ramp] = RateLimiter.TokenBucket({ - rate: update.rateLimiterConfig.rate, - capacity: update.rateLimiterConfig.capacity, - tokens: update.rateLimiterConfig.capacity, - lastUpdated: uint32(block.timestamp), - isEnabled: update.rateLimiterConfig.isEnabled - }); - emit OnRampAdded(update.ramp, update.rateLimiterConfig); - } else { - revert RampAlreadyExists(update.ramp); - } - } else { - if (s_onRamps.remove(update.ramp)) { - delete s_onRampRateLimits[update.ramp]; - emit OnRampRemoved(update.ramp); - } else { - // Cannot remove a non-existent onRamp. - revert NonExistentRamp(update.ramp); - } - } - } - - for (uint256 i = 0; i < offRamps.length; ++i) { - RampUpdate memory update = offRamps[i]; - if (update.allowed) { - if (s_offRamps.add(update.ramp)) { - s_offRampRateLimits[update.ramp] = RateLimiter.TokenBucket({ - rate: update.rateLimiterConfig.rate, - capacity: update.rateLimiterConfig.capacity, - tokens: update.rateLimiterConfig.capacity, - lastUpdated: uint32(block.timestamp), - isEnabled: update.rateLimiterConfig.isEnabled - }); - emit OffRampAdded(update.ramp, update.rateLimiterConfig); - } else { - revert RampAlreadyExists(update.ramp); - } - } else { - if (s_offRamps.remove(update.ramp)) { - delete s_offRampRateLimits[update.ramp]; - emit OffRampRemoved(update.ramp); - } else { - // Cannot remove a non-existent offRamp. - revert NonExistentRamp(update.ramp); - } - } - } - } - - // ================================================================ - // │ Rate limiting │ - // ================================================================ - - /// @notice Consumes outbound rate limiting capacity in this pool - function _consumeOnRampRateLimit(uint256 amount) internal { - s_onRampRateLimits[msg.sender]._consume(amount, address(i_token)); - } - - /// @notice Consumes inbound rate limiting capacity in this pool - function _consumeOffRampRateLimit(uint256 amount) internal { - s_offRampRateLimits[msg.sender]._consume(amount, address(i_token)); - } - - /// @notice Gets the token bucket with its values for the block it was requested at. - /// @return The token bucket. - function currentOnRampRateLimiterState(address onRamp) external view returns (RateLimiter.TokenBucket memory) { - return s_onRampRateLimits[onRamp]._currentTokenBucketState(); - } - - /// @notice Gets the token bucket with its values for the block it was requested at. - /// @return The token bucket. - function currentOffRampRateLimiterState(address offRamp) external view returns (RateLimiter.TokenBucket memory) { - return s_offRampRateLimits[offRamp]._currentTokenBucketState(); - } - - /// @notice Sets the onramp rate limited config. - /// @param config The new rate limiter config. - function setOnRampRateLimiterConfig(address onRamp, RateLimiter.Config memory config) external onlyOwner { - if (!isOnRamp(onRamp)) revert NonExistentRamp(onRamp); - s_onRampRateLimits[onRamp]._setTokenBucketConfig(config); - emit OnRampConfigured(onRamp, config); - } - - /// @notice Sets the offramp rate limited config. - /// @param config The new rate limiter config. - function setOffRampRateLimiterConfig(address offRamp, RateLimiter.Config memory config) external onlyOwner { - if (!isOffRamp(offRamp)) revert NonExistentRamp(offRamp); - s_offRampRateLimits[offRamp]._setTokenBucketConfig(config); - emit OffRampConfigured(offRamp, config); - } - - // ================================================================ - // │ Access │ - // ================================================================ - - /// @notice Checks whether the msg.sender is a permissioned onRamp on this contract - /// @dev Reverts with a PermissionsError if check fails - modifier onlyOnRamp() { - if (!isOnRamp(msg.sender)) revert PermissionsError(); - _; - } - - /// @notice Checks whether the msg.sender is a permissioned offRamp on this contract - /// @dev Reverts with a PermissionsError if check fails - modifier onlyOffRamp() { - if (!isOffRamp(msg.sender)) revert PermissionsError(); - _; - } - - // ================================================================ - // │ Allowlist │ - // ================================================================ - - modifier checkAllowList(address sender) { - if (i_allowlistEnabled && !s_allowList.contains(sender)) revert SenderNotAllowed(sender); - _; - } - - /// @notice Gets whether the allowList functionality is enabled. - /// @return true is enabled, false if not. - function getAllowListEnabled() external view returns (bool) { - return i_allowlistEnabled; - } - - /// @notice Gets the allowed addresses. - /// @return The allowed addresses. - function getAllowList() external view returns (address[] memory) { - return s_allowList.values(); - } - - /// @notice Apply updates to the allow list. - /// @param removes The addresses to be removed. - /// @param adds The addresses to be added. - /// @dev allowListing will be removed before public launch - function applyAllowListUpdates(address[] calldata removes, address[] calldata adds) external onlyOwner { - _applyAllowListUpdates(removes, adds); - } - - /// @notice Internal version of applyAllowListUpdates to allow for reuse in the constructor. - function _applyAllowListUpdates(address[] memory removes, address[] memory adds) internal { - if (!i_allowlistEnabled) revert AllowListNotEnabled(); - - for (uint256 i = 0; i < removes.length; ++i) { - address toRemove = removes[i]; - if (s_allowList.remove(toRemove)) { - emit AllowListRemove(toRemove); - } - } - for (uint256 i = 0; i < adds.length; ++i) { - address toAdd = adds[i]; - if (toAdd == address(0)) { - continue; - } - if (s_allowList.add(toAdd)) { - emit AllowListAdd(toAdd); - } - } - } - - /// @notice Ensure that there is no active curse. - modifier whenHealthy() { - if (IRMN(i_armProxy).isCursed()) revert BadARMSignal(); - _; - } -} - -contract BurnMintTokenPool1_2 is ITypeAndVersion, TokenPool1_2 { - // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables - string public constant override typeAndVersion = "BurnMintTokenPool 1.2.0"; - - constructor( - IBurnMintERC20 token, - address[] memory allowlist, - address armProxy - ) TokenPool1_2(token, allowlist, armProxy) {} - - /// @notice Burn the token in the pool - /// @param amount Amount to burn - /// @dev The whenHealthy check is important to ensure that even if a ramp is compromised - /// we're able to stop token movement via ARM. - function lockOrBurn( - address originalSender, - bytes calldata, - uint256 amount, - uint64, - bytes calldata - ) external virtual override onlyOnRamp checkAllowList(originalSender) whenHealthy returns (bytes memory) { - _consumeOnRampRateLimit(amount); - IBurnMintERC20(address(i_token)).burn(amount); - emit Burned(msg.sender, amount); - return ""; - } - - /// @notice Mint tokens from the pool to the recipient - /// @param receiver Recipient address - /// @param amount Amount to mint - /// @dev The whenHealthy check is important to ensure that even if a ramp is compromised - /// we're able to stop token movement via ARM. - function releaseOrMint( - bytes memory, - address receiver, - uint256 amount, - uint64, - bytes memory - ) external virtual override whenHealthy onlyOffRamp { - _consumeOffRampRateLimit(amount); - IBurnMintERC20(address(i_token)).mint(receiver, amount); - emit Minted(msg.sender, receiver, amount); - } -} diff --git a/contracts/src/v0.8/ccip/test/legacy/BurnMintTokenPool1_4.sol b/contracts/src/v0.8/ccip/test/legacy/BurnMintTokenPool1_4.sol deleted file mode 100644 index 168afee4f06..00000000000 --- a/contracts/src/v0.8/ccip/test/legacy/BurnMintTokenPool1_4.sol +++ /dev/null @@ -1,398 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol"; -import {IBurnMintERC20} from "../../../shared/token/ERC20/IBurnMintERC20.sol"; -import {IPoolPriorTo1_5} from "../../interfaces/IPoolPriorTo1_5.sol"; -import {IRMN} from "../../interfaces/IRMN.sol"; -import {IRouter} from "../../interfaces/IRouter.sol"; - -import {OwnerIsCreator} from "../../../shared/access/OwnerIsCreator.sol"; -import {RateLimiter} from "../../libraries/RateLimiter.sol"; - -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {IERC165} from "../../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/introspection/IERC165.sol"; -import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/structs/EnumerableSet.sol"; - -/// @notice Base abstract class with common functions for all token pools. -/// A token pool serves as isolated place for holding tokens and token specific logic -/// that may execute as tokens move across the bridge. -abstract contract TokenPool1_4 is IPoolPriorTo1_5, OwnerIsCreator, IERC165 { - using EnumerableSet for EnumerableSet.AddressSet; - using EnumerableSet for EnumerableSet.UintSet; - using RateLimiter for RateLimiter.TokenBucket; - - error CallerIsNotARampOnRouter(address caller); - error ZeroAddressNotAllowed(); - error SenderNotAllowed(address sender); - error AllowListNotEnabled(); - error NonExistentChain(uint64 remoteChainSelector); - error ChainNotAllowed(uint64 remoteChainSelector); - error BadARMSignal(); - error ChainAlreadyExists(uint64 chainSelector); - - event Locked(address indexed sender, uint256 amount); - event Burned(address indexed sender, uint256 amount); - event Released(address indexed sender, address indexed recipient, uint256 amount); - event Minted(address indexed sender, address indexed recipient, uint256 amount); - event ChainAdded( - uint64 remoteChainSelector, - RateLimiter.Config outboundRateLimiterConfig, - RateLimiter.Config inboundRateLimiterConfig - ); - event ChainConfigured( - uint64 remoteChainSelector, - RateLimiter.Config outboundRateLimiterConfig, - RateLimiter.Config inboundRateLimiterConfig - ); - event ChainRemoved(uint64 remoteChainSelector); - event AllowListAdd(address sender); - event AllowListRemove(address sender); - event RouterUpdated(address oldRouter, address newRouter); - - struct ChainUpdate { - uint64 remoteChainSelector; // ──╮ Remote chain selector - bool allowed; // ────────────────╯ Whether the chain is allowed - RateLimiter.Config outboundRateLimiterConfig; // Outbound rate limited config, meaning the rate limits for all of the onRamps for the given chain - RateLimiter.Config inboundRateLimiterConfig; // Inbound rate limited config, meaning the rate limits for all of the offRamps for the given chain - } - - /// @dev The bridgeable token that is managed by this pool. - IERC20 internal immutable i_token; - /// @dev The address of the arm proxy - address internal immutable i_armProxy; - /// @dev The immutable flag that indicates if the pool is access-controlled. - bool internal immutable i_allowlistEnabled; - /// @dev A set of addresses allowed to trigger lockOrBurn as original senders. - /// Only takes effect if i_allowlistEnabled is true. - /// This can be used to ensure only token-issuer specified addresses can - /// move tokens. - EnumerableSet.AddressSet internal s_allowList; - /// @dev The address of the router - IRouter internal s_router; - /// @dev A set of allowed chain selectors. We want the allowlist to be enumerable to - /// be able to quickly determine (without parsing logs) who can access the pool. - /// @dev The chain selectors are in uin256 format because of the EnumerableSet implementation. - EnumerableSet.UintSet internal s_remoteChainSelectors; - /// @dev Outbound rate limits. Corresponds to the inbound rate limit for the pool - /// on the remote chain. - mapping(uint64 remoteChainSelector => RateLimiter.TokenBucket) internal s_outboundRateLimits; - /// @dev Inbound rate limits. This allows per destination chain - /// token issuer specified rate limiting (e.g. issuers may trust chains to varying - /// degrees and prefer different limits) - mapping(uint64 remoteChainSelector => RateLimiter.TokenBucket) internal s_inboundRateLimits; - - constructor(IERC20 token, address[] memory allowlist, address armProxy, address router) { - if (address(token) == address(0) || router == address(0)) revert ZeroAddressNotAllowed(); - i_token = token; - i_armProxy = armProxy; - s_router = IRouter(router); - - // Pool can be set as permissioned or permissionless at deployment time only to save hot-path gas. - i_allowlistEnabled = allowlist.length > 0; - if (i_allowlistEnabled) { - _applyAllowListUpdates(new address[](0), allowlist); - } - } - - /// @notice Get ARM proxy address - /// @return armProxy Address of arm proxy - function getArmProxy() public view returns (address armProxy) { - return i_armProxy; - } - - /// @inheritdoc IPoolPriorTo1_5 - function getToken() public view override returns (IERC20 token) { - return i_token; - } - - /// @notice Gets the pool's Router - /// @return router The pool's Router - function getRouter() public view returns (address router) { - return address(s_router); - } - - /// @notice Sets the pool's Router - /// @param newRouter The new Router - function setRouter(address newRouter) public onlyOwner { - if (newRouter == address(0)) revert ZeroAddressNotAllowed(); - address oldRouter = address(s_router); - s_router = IRouter(newRouter); - - emit RouterUpdated(oldRouter, newRouter); - } - - /// @inheritdoc IERC165 - function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) { - return interfaceId == type(IPoolPriorTo1_5).interfaceId || interfaceId == type(IERC165).interfaceId; - } - - // ================================================================ - // │ Chain permissions │ - // ================================================================ - - /// @notice Checks whether a chain selector is permissioned on this contract. - /// @return true if the given chain selector is a permissioned remote chain. - function isSupportedChain(uint64 remoteChainSelector) public view returns (bool) { - return s_remoteChainSelectors.contains(remoteChainSelector); - } - - /// @notice Get list of allowed chains - /// @return list of chains. - function getSupportedChains() public view returns (uint64[] memory) { - uint256[] memory uint256ChainSelectors = s_remoteChainSelectors.values(); - uint64[] memory chainSelectors = new uint64[](uint256ChainSelectors.length); - for (uint256 i = 0; i < uint256ChainSelectors.length; ++i) { - chainSelectors[i] = uint64(uint256ChainSelectors[i]); - } - - return chainSelectors; - } - - /// @notice Sets the permissions for a list of chains selectors. Actual senders for these chains - /// need to be allowed on the Router to interact with this pool. - /// @dev Only callable by the owner - /// @param chains A list of chains and their new permission status & rate limits. Rate limits - /// are only used when the chain is being added through `allowed` being true. - function applyChainUpdates(ChainUpdate[] calldata chains) external virtual onlyOwner { - for (uint256 i = 0; i < chains.length; ++i) { - ChainUpdate memory update = chains[i]; - RateLimiter._validateTokenBucketConfig(update.outboundRateLimiterConfig, !update.allowed); - RateLimiter._validateTokenBucketConfig(update.inboundRateLimiterConfig, !update.allowed); - - if (update.allowed) { - // If the chain already exists, revert - if (!s_remoteChainSelectors.add(update.remoteChainSelector)) { - revert ChainAlreadyExists(update.remoteChainSelector); - } - - s_outboundRateLimits[update.remoteChainSelector] = RateLimiter.TokenBucket({ - rate: update.outboundRateLimiterConfig.rate, - capacity: update.outboundRateLimiterConfig.capacity, - tokens: update.outboundRateLimiterConfig.capacity, - lastUpdated: uint32(block.timestamp), - isEnabled: update.outboundRateLimiterConfig.isEnabled - }); - - s_inboundRateLimits[update.remoteChainSelector] = RateLimiter.TokenBucket({ - rate: update.inboundRateLimiterConfig.rate, - capacity: update.inboundRateLimiterConfig.capacity, - tokens: update.inboundRateLimiterConfig.capacity, - lastUpdated: uint32(block.timestamp), - isEnabled: update.inboundRateLimiterConfig.isEnabled - }); - emit ChainAdded(update.remoteChainSelector, update.outboundRateLimiterConfig, update.inboundRateLimiterConfig); - } else { - // If the chain doesn't exist, revert - if (!s_remoteChainSelectors.remove(update.remoteChainSelector)) { - revert NonExistentChain(update.remoteChainSelector); - } - - delete s_inboundRateLimits[update.remoteChainSelector]; - delete s_outboundRateLimits[update.remoteChainSelector]; - emit ChainRemoved(update.remoteChainSelector); - } - } - } - - // ================================================================ - // │ Rate limiting │ - // ================================================================ - - /// @notice Consumes outbound rate limiting capacity in this pool - function _consumeOutboundRateLimit(uint64 remoteChainSelector, uint256 amount) internal { - s_outboundRateLimits[remoteChainSelector]._consume(amount, address(i_token)); - } - - /// @notice Consumes inbound rate limiting capacity in this pool - function _consumeInboundRateLimit(uint64 remoteChainSelector, uint256 amount) internal { - s_inboundRateLimits[remoteChainSelector]._consume(amount, address(i_token)); - } - - /// @notice Gets the token bucket with its values for the block it was requested at. - /// @return The token bucket. - function getCurrentOutboundRateLimiterState( - uint64 remoteChainSelector - ) external view returns (RateLimiter.TokenBucket memory) { - return s_outboundRateLimits[remoteChainSelector]._currentTokenBucketState(); - } - - /// @notice Gets the token bucket with its values for the block it was requested at. - /// @return The token bucket. - function getCurrentInboundRateLimiterState( - uint64 remoteChainSelector - ) external view returns (RateLimiter.TokenBucket memory) { - return s_inboundRateLimits[remoteChainSelector]._currentTokenBucketState(); - } - - /// @notice Sets the chain rate limiter config. - /// @param remoteChainSelector The remote chain selector for which the rate limits apply. - /// @param outboundConfig The new outbound rate limiter config, meaning the onRamp rate limits for the given chain. - /// @param inboundConfig The new inbound rate limiter config, meaning the offRamp rate limits for the given chain. - function setChainRateLimiterConfig( - uint64 remoteChainSelector, - RateLimiter.Config memory outboundConfig, - RateLimiter.Config memory inboundConfig - ) external virtual onlyOwner { - _setRateLimitConfig(remoteChainSelector, outboundConfig, inboundConfig); - } - - function _setRateLimitConfig( - uint64 remoteChainSelector, - RateLimiter.Config memory outboundConfig, - RateLimiter.Config memory inboundConfig - ) internal { - if (!isSupportedChain(remoteChainSelector)) revert NonExistentChain(remoteChainSelector); - RateLimiter._validateTokenBucketConfig(outboundConfig, false); - s_outboundRateLimits[remoteChainSelector]._setTokenBucketConfig(outboundConfig); - RateLimiter._validateTokenBucketConfig(inboundConfig, false); - s_inboundRateLimits[remoteChainSelector]._setTokenBucketConfig(inboundConfig); - emit ChainConfigured(remoteChainSelector, outboundConfig, inboundConfig); - } - - // ================================================================ - // │ Access │ - // ================================================================ - - /// @notice Checks whether remote chain selector is configured on this contract, and if the msg.sender - /// is a permissioned onRamp for the given chain on the Router. - modifier onlyOnRamp(uint64 remoteChainSelector) { - if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); - if (!(msg.sender == s_router.getOnRamp(remoteChainSelector))) revert CallerIsNotARampOnRouter(msg.sender); - _; - } - - /// @notice Checks whether remote chain selector is configured on this contract, and if the msg.sender - /// is a permissioned offRamp for the given chain on the Router. - modifier onlyOffRamp(uint64 remoteChainSelector) { - if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector); - if (!s_router.isOffRamp(remoteChainSelector, msg.sender)) revert CallerIsNotARampOnRouter(msg.sender); - _; - } - - // ================================================================ - // │ Allowlist │ - // ================================================================ - - modifier checkAllowList(address sender) { - if (i_allowlistEnabled && !s_allowList.contains(sender)) revert SenderNotAllowed(sender); - _; - } - - /// @notice Gets whether the allowList functionality is enabled. - /// @return true is enabled, false if not. - function getAllowListEnabled() external view returns (bool) { - return i_allowlistEnabled; - } - - /// @notice Gets the allowed addresses. - /// @return The allowed addresses. - function getAllowList() external view returns (address[] memory) { - return s_allowList.values(); - } - - /// @notice Apply updates to the allow list. - /// @param removes The addresses to be removed. - /// @param adds The addresses to be added. - /// @dev allowListing will be removed before public launch - function applyAllowListUpdates(address[] calldata removes, address[] calldata adds) external onlyOwner { - _applyAllowListUpdates(removes, adds); - } - - /// @notice Internal version of applyAllowListUpdates to allow for reuse in the constructor. - function _applyAllowListUpdates(address[] memory removes, address[] memory adds) internal { - if (!i_allowlistEnabled) revert AllowListNotEnabled(); - - for (uint256 i = 0; i < removes.length; ++i) { - address toRemove = removes[i]; - if (s_allowList.remove(toRemove)) { - emit AllowListRemove(toRemove); - } - } - for (uint256 i = 0; i < adds.length; ++i) { - address toAdd = adds[i]; - if (toAdd == address(0)) { - continue; - } - if (s_allowList.add(toAdd)) { - emit AllowListAdd(toAdd); - } - } - } - - /// @notice Ensure that there is no active curse. - modifier whenHealthy() { - if (IRMN(i_armProxy).isCursed()) revert BadARMSignal(); - _; - } -} - -abstract contract BurnMintTokenPoolAbstract is TokenPool1_4 { - /// @notice Contains the specific burn call for a pool. - /// @dev overriding this method allows us to create pools with different burn signatures - /// without duplicating the underlying logic. - function _burn(uint256 amount) internal virtual; - - /// @notice Burn the token in the pool - /// @param amount Amount to burn - /// @dev The whenHealthy check is important to ensure that even if a ramp is compromised - /// we're able to stop token movement via ARM. - function lockOrBurn( - address originalSender, - bytes calldata, - uint256 amount, - uint64 remoteChainSelector, - bytes calldata - ) - external - virtual - override - onlyOnRamp(remoteChainSelector) - checkAllowList(originalSender) - whenHealthy - returns (bytes memory) - { - _consumeOutboundRateLimit(remoteChainSelector, amount); - _burn(amount); - emit Burned(msg.sender, amount); - return ""; - } - - /// @notice Mint tokens from the pool to the recipient - /// @param receiver Recipient address - /// @param amount Amount to mint - /// @dev The whenHealthy check is important to ensure that even if a ramp is compromised - /// we're able to stop token movement via ARM. - function releaseOrMint( - bytes memory, - address receiver, - uint256 amount, - uint64 remoteChainSelector, - bytes memory - ) external virtual override whenHealthy onlyOffRamp(remoteChainSelector) { - _consumeInboundRateLimit(remoteChainSelector, amount); - IBurnMintERC20(address(i_token)).mint(receiver, amount); - emit Minted(msg.sender, receiver, amount); - } -} - -/// @notice This pool mints and burns a 3rd-party token. -/// @dev Pool whitelisting mode is set in the constructor and cannot be modified later. -/// It either accepts any address as originalSender, or only accepts whitelisted originalSender. -/// The only way to change whitelisting mode is to deploy a new pool. -/// If that is expected, please make sure the token's burner/minter roles are adjustable. -contract BurnMintTokenPool1_4 is BurnMintTokenPoolAbstract, ITypeAndVersion { - string public constant override typeAndVersion = "BurnMintTokenPool 1.4.0"; - - constructor( - IBurnMintERC20 token, - address[] memory allowlist, - address armProxy, - address router - ) TokenPool1_4(token, allowlist, armProxy, router) {} - - /// @inheritdoc BurnMintTokenPoolAbstract - function _burn(uint256 amount) internal virtual override { - IBurnMintERC20(address(i_token)).burn(amount); - } -} diff --git a/contracts/src/v0.8/ccip/test/legacy/TokenPoolAndProxy.t.sol b/contracts/src/v0.8/ccip/test/legacy/TokenPoolAndProxy.t.sol deleted file mode 100644 index 9645d70b7a6..00000000000 --- a/contracts/src/v0.8/ccip/test/legacy/TokenPoolAndProxy.t.sol +++ /dev/null @@ -1,671 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; - -import {IPoolV1} from "../../interfaces/IPool.sol"; -import {IPoolPriorTo1_5} from "../../interfaces/IPoolPriorTo1_5.sol"; - -import {BurnMintERC677} from "../../../shared/token/ERC677/BurnMintERC677.sol"; -import {Router} from "../../Router.sol"; -import {Client} from "../../libraries/Client.sol"; -import {Pool} from "../../libraries/Pool.sol"; -import {RateLimiter} from "../../libraries/RateLimiter.sol"; -import {BurnMintTokenPoolAndProxy} from "../../pools/BurnMintTokenPoolAndProxy.sol"; -import {BurnWithFromMintTokenPoolAndProxy} from "../../pools/BurnWithFromMintTokenPoolAndProxy.sol"; -import {LockReleaseTokenPoolAndProxy} from "../../pools/LockReleaseTokenPoolAndProxy.sol"; -import {TokenPool} from "../../pools/TokenPool.sol"; -import {EVM2EVMOnRampSetup} from "../onRamp/EVM2EVMOnRampSetup.t.sol"; -import {RouterSetup} from "../router/RouterSetup.t.sol"; -import {BurnMintTokenPool1_2, TokenPool1_2} from "./BurnMintTokenPool1_2.sol"; -import {BurnMintTokenPool1_4, TokenPool1_4} from "./BurnMintTokenPool1_4.sol"; - -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {IERC165} from "../../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/introspection/IERC165.sol"; - -contract TokenPoolAndProxyMigration is EVM2EVMOnRampSetup { - BurnMintTokenPoolAndProxy internal s_newPool; - IPoolPriorTo1_5 internal s_legacyPool; - BurnMintERC677 internal s_token; - - address internal s_offRamp; - address internal s_sourcePool = makeAddr("source_pool"); - address internal s_sourceToken = makeAddr("source_token"); - uint256 internal constant AMOUNT = 1; - - function setUp() public virtual override { - super.setUp(); - // Create a system with a token and a legacy pool - s_token = new BurnMintERC677("Test", "TEST", 18, type(uint256).max); - // dealing doesn't update the total supply, meaning the first time we burn a token we underflow, which isn't - // guarded against. Then, when we mint a token, we overflow, which is guarded against and will revert. - s_token.grantMintAndBurnRoles(OWNER); - s_token.mint(OWNER, 1e18); - - s_offRamp = s_offRamps[0]; - // Approve enough for a few calls - s_token.approve(address(s_sourceRouter), AMOUNT * 100); - - // Approve infinite fee tokens - IERC20(s_sourceFeeToken).approve(address(s_sourceRouter), type(uint256).max); - } - - /// @notice This test covers the entire migration plan for 1.0-1.2 pools to 1.5 pools. For simplicity - /// we will refer to the 1.0/1.2 pools as 1.2 pools, as they are functionally the same. - function test_tokenPoolMigration_Success_1_2() public { - // ================================================================ - // | 1 1.2 prior to upgrade | - // ================================================================ - _deployPool1_2(); - - // Ensure everything works on the 1.2 pool - _ccipSend_OLD(); - _fakeReleaseOrMintFromOffRamp_OLD(); - - // ================================================================ - // | 2 Deploy self serve | - // ================================================================ - _deploySelfServe(); - - // This doesn't impact the 1.2 pool, so it should still be functional - _ccipSend_OLD(); - _fakeReleaseOrMintFromOffRamp_OLD(); - - // ================================================================ - // | 3 Configure new pool on old pool | - // ================================================================ - // In the 1.2 case, everything keeps working on both the 1.2 and 1.5 pools. This config can be - // done in advance of the actual swap to 1.5 lanes. - vm.startPrank(OWNER); - TokenPool1_2.RampUpdate[] memory rampUpdates = new TokenPool1_2.RampUpdate[](1); - rampUpdates[0] = TokenPool1_2.RampUpdate({ - ramp: address(s_newPool), - allowed: true, - // The rate limits should be turned off for this fake ramp, as the 1.5 pool will handle all the - // rate limiting for us. - rateLimiterConfig: RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}) - }); - // Since this call doesn't impact the usability of the old pool, we can do it whenever we want - BurnMintTokenPool1_2(address(s_legacyPool)).applyRampUpdates(rampUpdates, rampUpdates); - - // Assert the 1.2 lanes still work - _ccipSend_OLD(); - _fakeReleaseOrMintFromOffRamp_OLD(); - - // ================================================================ - // | 4 Update the router with to 1.5 | - // ================================================================ - - // This will stop any new messages entering the old lanes, and will direct all traffic to the - // new 1.5 lanes, and therefore to the 1.5 pools. Note that the old pools will still receive - // inflight messages, and will need to continue functioning until all of those are processed. - _fakeReleaseOrMintFromOffRamp_OLD(); - - // Everything is configured, we can now send a ccip tx to the new pool - _ccipSend1_5(); - _fakeReleaseOrMintFromOffRamp1_5(); - - // ================================================================ - // | 5 Migrate to using 1.5 the pool | - // ================================================================ - // Turn off the legacy pool, this enabled the 1.5 pool logic. This should be done AFTER the new pool - // has gotten permissions to mint/burn. We see the case where that isn't done below. - vm.startPrank(OWNER); - s_newPool.setPreviousPool(IPoolPriorTo1_5(address(0))); - - // The new pool is now active, but is has not been given permissions to burn/mint yet - vm.expectRevert(abi.encodeWithSelector(BurnMintERC677.SenderNotBurner.selector, address(s_newPool))); - _ccipSend1_5(); - vm.expectRevert(abi.encodeWithSelector(BurnMintERC677.SenderNotMinter.selector, address(s_newPool))); - _fakeReleaseOrMintFromOffRamp1_5(); - - // When we do give burn/mint, the new pool is fully active - vm.startPrank(OWNER); - s_token.grantMintAndBurnRoles(address(s_newPool)); - _ccipSend1_5(); - _fakeReleaseOrMintFromOffRamp1_5(); - - // Even after the pool has taken over as primary, the old pool can still process messages from the old lane - _fakeReleaseOrMintFromOffRamp_OLD(); - } - - function test_tokenPoolMigration_Success_1_4() public { - // ================================================================ - // | 1 1.4 prior to upgrade | - // ================================================================ - _deployPool1_4(); - - // Ensure everything works on the 1.4 pool - _ccipSend_OLD(); - _fakeReleaseOrMintFromOffRamp_OLD(); - - // ================================================================ - // | 2 Deploy self serve | - // ================================================================ - _deploySelfServe(); - - // This doesn't impact the 1.4 pool, so it should still be functional - _ccipSend_OLD(); - _fakeReleaseOrMintFromOffRamp_OLD(); - - // ================================================================ - // | 3 Configure new pool on old pool | - // | AND | - // | Update the router with to 1.5 | - // ================================================================ - // NOTE: when this call is made, the SENDING SIDE of old lanes stop working. - vm.startPrank(OWNER); - BurnMintTokenPool1_4(address(s_legacyPool)).setRouter(address(s_newPool)); - - // This will stop any new messages entering the old lanes, and will direct all traffic to the - // new 1.5 lanes, and therefore to the 1.5 pools. Note that the old pools will still receive - // inflight messages, and will need to continue functioning until all of those are processed. - _fakeReleaseOrMintFromOffRamp_OLD(); - - // Sending to the old 1.4 pool no longer works - _ccipSend_OLD_Reverts(); - - // Everything is configured, we can now send a ccip tx - _ccipSend1_5(); - _fakeReleaseOrMintFromOffRamp1_5(); - - // ================================================================ - // | 4 Migrate to using 1.5 the pool | - // ================================================================ - // Turn off the legacy pool, this enabled the 1.5 pool logic. This should be done AFTER the new pool - // has gotten permissions to mint/burn. We see the case where that isn't done below. - vm.startPrank(OWNER); - s_newPool.setPreviousPool(IPoolPriorTo1_5(address(0))); - - // The new pool is now active, but is has not been given permissions to burn/mint yet - vm.expectRevert(abi.encodeWithSelector(BurnMintERC677.SenderNotBurner.selector, address(s_newPool))); - _ccipSend1_5(); - vm.expectRevert(abi.encodeWithSelector(BurnMintERC677.SenderNotMinter.selector, address(s_newPool))); - _fakeReleaseOrMintFromOffRamp1_5(); - - // When we do give burn/mint, the new pool is fully active - vm.startPrank(OWNER); - s_token.grantMintAndBurnRoles(address(s_newPool)); - _ccipSend1_5(); - _fakeReleaseOrMintFromOffRamp1_5(); - - // Even after the pool has taken over as primary, the old pool can still process messages from the old lane - _fakeReleaseOrMintFromOffRamp_OLD(); - } - - function _ccipSend_OLD() internal { - // We send the funds to the pool manually, as the ramp normally does that - deal(address(s_token), address(s_legacyPool), AMOUNT); - vm.startPrank(address(s_onRamp)); - s_legacyPool.lockOrBurn(OWNER, abi.encode(OWNER), AMOUNT, DEST_CHAIN_SELECTOR, ""); - } - - function _ccipSend_OLD_Reverts() internal { - // We send the funds to the pool manually, as the ramp normally does that - deal(address(s_token), address(s_legacyPool), AMOUNT); - vm.startPrank(address(s_onRamp)); - - vm.expectRevert(abi.encodeWithSelector(TokenPool1_4.CallerIsNotARampOnRouter.selector, address(s_onRamp))); - - s_legacyPool.lockOrBurn(OWNER, abi.encode(OWNER), AMOUNT, DEST_CHAIN_SELECTOR, ""); - } - - function _ccipSend1_5() internal { - vm.startPrank(address(OWNER)); - Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); - tokenAmounts[0] = Client.EVMTokenAmount({token: address(s_token), amount: AMOUNT}); - - s_sourceRouter.ccipSend( - DEST_CHAIN_SELECTOR, - Client.EVM2AnyMessage({ - receiver: abi.encode(OWNER), - data: "", - tokenAmounts: tokenAmounts, - feeToken: s_sourceFeeToken, - extraArgs: "" - }) - ); - } - - function _fakeReleaseOrMintFromOffRamp1_5() internal { - // This is a fake call to simulate the release or mint from the "offRamp" - vm.startPrank(s_offRamp); - s_newPool.releaseOrMint( - Pool.ReleaseOrMintInV1({ - originalSender: abi.encode(OWNER), - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - receiver: OWNER, - amount: AMOUNT, - localToken: address(s_token), - sourcePoolAddress: abi.encode(s_sourcePool), - sourcePoolData: "", - offchainTokenData: "" - }) - ); - } - - function _fakeReleaseOrMintFromOffRamp_OLD() internal { - // This is a fake call to simulate the release or mint from the "offRamp" - vm.startPrank(s_offRamp); - s_legacyPool.releaseOrMint(abi.encode(OWNER), OWNER, AMOUNT, SOURCE_CHAIN_SELECTOR, ""); - } - - function _deployPool1_2() internal { - vm.startPrank(OWNER); - s_legacyPool = new BurnMintTokenPool1_2(s_token, new address[](0), address(s_mockRMN)); - s_token.grantMintAndBurnRoles(address(s_legacyPool)); - - TokenPool1_2.RampUpdate[] memory onRampUpdates = new TokenPool1_2.RampUpdate[](1); - onRampUpdates[0] = TokenPool1_2.RampUpdate({ - ramp: address(s_onRamp), - allowed: true, - rateLimiterConfig: _getInboundRateLimiterConfig() - }); - TokenPool1_2.RampUpdate[] memory offRampUpdates = new TokenPool1_2.RampUpdate[](1); - offRampUpdates[0] = TokenPool1_2.RampUpdate({ - ramp: address(s_offRamp), - allowed: true, - rateLimiterConfig: _getInboundRateLimiterConfig() - }); - BurnMintTokenPool1_2(address(s_legacyPool)).applyRampUpdates(onRampUpdates, offRampUpdates); - } - - function _deployPool1_4() internal { - vm.startPrank(OWNER); - s_legacyPool = new BurnMintTokenPool1_4(s_token, new address[](0), address(s_mockRMN), address(s_sourceRouter)); - s_token.grantMintAndBurnRoles(address(s_legacyPool)); - - TokenPool1_4.ChainUpdate[] memory legacyChainUpdates = new TokenPool1_4.ChainUpdate[](2); - legacyChainUpdates[0] = TokenPool1_4.ChainUpdate({ - remoteChainSelector: DEST_CHAIN_SELECTOR, - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - legacyChainUpdates[1] = TokenPool1_4.ChainUpdate({ - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - BurnMintTokenPool1_4(address(s_legacyPool)).applyChainUpdates(legacyChainUpdates); - } - - function _deploySelfServe() internal { - vm.startPrank(OWNER); - // Deploy the new pool - s_newPool = new BurnMintTokenPoolAndProxy(s_token, new address[](0), address(s_mockRMN), address(s_sourceRouter)); - // Set the previous pool on the new pool - s_newPool.setPreviousPool(s_legacyPool); - - // Configure the lanes just like the legacy pool - TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](2); - chainUpdates[0] = TokenPool.ChainUpdate({ - remoteChainSelector: DEST_CHAIN_SELECTOR, - remotePoolAddress: abi.encode(s_destTokenPool), - remoteTokenAddress: abi.encode(s_destToken), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - chainUpdates[1] = TokenPool.ChainUpdate({ - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - remotePoolAddress: abi.encode(s_sourcePool), - remoteTokenAddress: abi.encode(s_sourceToken), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - s_newPool.applyChainUpdates(chainUpdates); - - // Register the token on the token admin registry - s_tokenAdminRegistry.proposeAdministrator(address(s_token), OWNER); - // Accept ownership of the token - s_tokenAdminRegistry.acceptAdminRole(address(s_token)); - // Set the pool on the admin registry - s_tokenAdminRegistry.setPool(address(s_token), address(s_newPool)); - } -} - -contract TokenPoolAndProxy is EVM2EVMOnRampSetup { - event Burned(address indexed sender, uint256 amount); - event Minted(address indexed sender, address indexed recipient, uint256 amount); - - IPoolV1 internal s_pool; - BurnMintERC677 internal s_token; - IPoolPriorTo1_5 internal s_legacyPool; - address internal s_fakeOffRamp = makeAddr("off_ramp"); - - address internal s_destPool = makeAddr("dest_pool"); - - function setUp() public virtual override { - super.setUp(); - s_token = BurnMintERC677(s_sourceFeeToken); - - Router.OffRamp[] memory fakeOffRamps = new Router.OffRamp[](1); - fakeOffRamps[0] = Router.OffRamp({sourceChainSelector: DEST_CHAIN_SELECTOR, offRamp: s_fakeOffRamp}); - s_sourceRouter.applyRampUpdates(new Router.OnRamp[](0), new Router.OffRamp[](0), fakeOffRamps); - - s_token.grantMintAndBurnRoles(OWNER); - s_token.mint(OWNER, 1e18); - } - - function test_lockOrBurn_burnMint_Success() public { - s_pool = new BurnMintTokenPoolAndProxy(s_token, new address[](0), address(s_mockRMN), address(s_sourceRouter)); - _configurePool(); - _deployOldPool(); - _assertLockOrBurnCorrect(); - - vm.startPrank(OWNER); - BurnMintTokenPoolAndProxy(address(s_pool)).setPreviousPool(IPoolPriorTo1_5(address(0))); - - _assertReleaseOrMintCorrect(); - } - - function test_lockOrBurn_burnWithFromMint_Success() public { - s_pool = - new BurnWithFromMintTokenPoolAndProxy(s_token, new address[](0), address(s_mockRMN), address(s_sourceRouter)); - _configurePool(); - _deployOldPool(); - _assertLockOrBurnCorrect(); - - vm.startPrank(OWNER); - BurnMintTokenPoolAndProxy(address(s_pool)).setPreviousPool(IPoolPriorTo1_5(address(0))); - - _assertReleaseOrMintCorrect(); - } - - function test_lockOrBurn_lockRelease_Success() public { - s_pool = - new LockReleaseTokenPoolAndProxy(s_token, new address[](0), address(s_mockRMN), false, address(s_sourceRouter)); - _configurePool(); - _deployOldPool(); - _assertLockOrBurnCorrect(); - - vm.startPrank(OWNER); - BurnMintTokenPoolAndProxy(address(s_pool)).setPreviousPool(IPoolPriorTo1_5(address(0))); - - _assertReleaseOrMintCorrect(); - } - - function _deployOldPool() internal { - s_legacyPool = new BurnMintTokenPool1_2(s_token, new address[](0), address(s_mockRMN)); - s_token.grantMintAndBurnRoles(address(s_legacyPool)); - - TokenPool1_2.RampUpdate[] memory onRampUpdates = new TokenPool1_2.RampUpdate[](1); - onRampUpdates[0] = - TokenPool1_2.RampUpdate({ramp: address(s_pool), allowed: true, rateLimiterConfig: _getInboundRateLimiterConfig()}); - TokenPool1_2.RampUpdate[] memory offRampUpdates = new TokenPool1_2.RampUpdate[](1); - offRampUpdates[0] = - TokenPool1_2.RampUpdate({ramp: address(s_pool), allowed: true, rateLimiterConfig: _getInboundRateLimiterConfig()}); - BurnMintTokenPool1_2(address(s_legacyPool)).applyRampUpdates(onRampUpdates, offRampUpdates); - } - - function _configurePool() internal { - TokenPool.ChainUpdate[] memory chains = new TokenPool.ChainUpdate[](1); - chains[0] = TokenPool.ChainUpdate({ - remoteChainSelector: DEST_CHAIN_SELECTOR, - remotePoolAddress: abi.encode(s_destPool), - remoteTokenAddress: abi.encode(s_destToken), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - - BurnMintTokenPoolAndProxy(address(s_pool)).applyChainUpdates(chains); - - // CCIP Token Admin has already been registered from TokenSetup - s_tokenAdminRegistry.setPool(address(s_token), address(s_pool)); - - s_token.grantMintAndBurnRoles(address(s_pool)); - } - - function _assertLockOrBurnCorrect() internal { - uint256 amount = 1234; - vm.startPrank(address(s_onRamp)); - - // lockOrBurn, assert normal path is taken - deal(address(s_token), address(s_pool), amount); - - s_pool.lockOrBurn( - Pool.LockOrBurnInV1({ - receiver: abi.encode(OWNER), - remoteChainSelector: DEST_CHAIN_SELECTOR, - originalSender: OWNER, - amount: amount, - localToken: address(s_token) - }) - ); - - // set legacy pool - - vm.startPrank(OWNER); - BurnMintTokenPoolAndProxy(address(s_pool)).setPreviousPool(s_legacyPool); - - // lockOrBurn, assert legacy pool is called - - vm.startPrank(address(s_onRamp)); - deal(address(s_token), address(s_pool), amount); - - vm.expectEmit(address(s_legacyPool)); - emit Burned(address(s_pool), amount); - - s_pool.lockOrBurn( - Pool.LockOrBurnInV1({ - receiver: abi.encode(OWNER), - remoteChainSelector: DEST_CHAIN_SELECTOR, - originalSender: OWNER, - amount: amount, - localToken: address(s_token) - }) - ); - } - - function _assertReleaseOrMintCorrect() internal { - uint256 amount = 1234; - vm.startPrank(s_fakeOffRamp); - - // releaseOrMint, assert normal path is taken - deal(address(s_token), address(s_pool), amount); - - s_pool.releaseOrMint( - Pool.ReleaseOrMintInV1({ - receiver: OWNER, - remoteChainSelector: DEST_CHAIN_SELECTOR, - originalSender: abi.encode(OWNER), - amount: amount, - localToken: address(s_token), - sourcePoolAddress: abi.encode(s_destPool), - sourcePoolData: "", - offchainTokenData: "" - }) - ); - - // set legacy pool - - vm.startPrank(OWNER); - BurnMintTokenPoolAndProxy(address(s_pool)).setPreviousPool(s_legacyPool); - - // releaseOrMint, assert legacy pool is called - - vm.startPrank(address(s_fakeOffRamp)); - - vm.expectEmit(address(s_legacyPool)); - emit Minted(address(s_pool), address(OWNER), amount); - - s_pool.releaseOrMint( - Pool.ReleaseOrMintInV1({ - receiver: OWNER, - remoteChainSelector: DEST_CHAIN_SELECTOR, - originalSender: abi.encode(OWNER), - amount: amount, - localToken: address(s_token), - sourcePoolAddress: abi.encode(s_destPool), - sourcePoolData: "", - offchainTokenData: "" - }) - ); - } - - function test_setPreviousPool_Success() public { - LockReleaseTokenPoolAndProxy pool = - new LockReleaseTokenPoolAndProxy(s_token, new address[](0), address(s_mockRMN), true, address(s_sourceRouter)); - - assertEq(pool.getPreviousPool(), address(0)); - - address newLegacyPool = makeAddr("new_legacy_pool"); - - vm.startPrank(OWNER); - pool.setPreviousPool(IPoolPriorTo1_5(newLegacyPool)); - assertEq(pool.getPreviousPool(), address(newLegacyPool)); - } -} - -//// -/// Duplicated tests from LockReleaseTokenPool.t.sol -/// - -contract LockReleaseTokenPoolAndProxySetup is RouterSetup { - IERC20 internal s_token; - LockReleaseTokenPoolAndProxy internal s_lockReleaseTokenPoolAndProxy; - LockReleaseTokenPoolAndProxy internal s_lockReleaseTokenPoolAndProxyWithAllowList; - address[] internal s_allowedList; - - address internal s_allowedOnRamp = address(123); - address internal s_allowedOffRamp = address(234); - - address internal s_destPoolAddress = address(2736782345); - address internal s_sourcePoolAddress = address(53852352095); - - function setUp() public virtual override { - RouterSetup.setUp(); - s_token = new BurnMintERC677("LINK", "LNK", 18, 0); - deal(address(s_token), OWNER, type(uint256).max); - s_lockReleaseTokenPoolAndProxy = - new LockReleaseTokenPoolAndProxy(s_token, new address[](0), address(s_mockRMN), true, address(s_sourceRouter)); - - s_allowedList.push(USER_1); - s_allowedList.push(DUMMY_CONTRACT_ADDRESS); - s_lockReleaseTokenPoolAndProxyWithAllowList = - new LockReleaseTokenPoolAndProxy(s_token, s_allowedList, address(s_mockRMN), true, address(s_sourceRouter)); - - TokenPool.ChainUpdate[] memory chainUpdate = new TokenPool.ChainUpdate[](1); - chainUpdate[0] = TokenPool.ChainUpdate({ - remoteChainSelector: DEST_CHAIN_SELECTOR, - remotePoolAddress: abi.encode(s_destPoolAddress), - remoteTokenAddress: abi.encode(address(s_token)), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - - s_lockReleaseTokenPoolAndProxy.applyChainUpdates(chainUpdate); - s_lockReleaseTokenPoolAndProxyWithAllowList.applyChainUpdates(chainUpdate); - s_lockReleaseTokenPoolAndProxy.setRebalancer(OWNER); - - Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); - Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); - onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: s_allowedOnRamp}); - offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: s_allowedOffRamp}); - s_sourceRouter.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); - } -} - -contract LockReleaseTokenPoolAndProxy_setRebalancer is LockReleaseTokenPoolAndProxySetup { - function test_SetRebalancer_Success() public { - assertEq(address(s_lockReleaseTokenPoolAndProxy.getRebalancer()), OWNER); - s_lockReleaseTokenPoolAndProxy.setRebalancer(STRANGER); - assertEq(address(s_lockReleaseTokenPoolAndProxy.getRebalancer()), STRANGER); - } - - function test_SetRebalancer_Revert() public { - vm.startPrank(STRANGER); - - vm.expectRevert("Only callable by owner"); - s_lockReleaseTokenPoolAndProxy.setRebalancer(STRANGER); - } -} - -contract LockReleaseTokenPoolPoolAndProxy_canAcceptLiquidity is LockReleaseTokenPoolAndProxySetup { - function test_CanAcceptLiquidity_Success() public { - assertEq(true, s_lockReleaseTokenPoolAndProxy.canAcceptLiquidity()); - - s_lockReleaseTokenPoolAndProxy = - new LockReleaseTokenPoolAndProxy(s_token, new address[](0), address(s_mockRMN), false, address(s_sourceRouter)); - assertEq(false, s_lockReleaseTokenPoolAndProxy.canAcceptLiquidity()); - } -} - -contract LockReleaseTokenPoolPoolAndProxy_provideLiquidity is LockReleaseTokenPoolAndProxySetup { - function test_Fuzz_ProvideLiquidity_Success(uint256 amount) public { - uint256 balancePre = s_token.balanceOf(OWNER); - s_token.approve(address(s_lockReleaseTokenPoolAndProxy), amount); - - s_lockReleaseTokenPoolAndProxy.provideLiquidity(amount); - - assertEq(s_token.balanceOf(OWNER), balancePre - amount); - assertEq(s_token.balanceOf(address(s_lockReleaseTokenPoolAndProxy)), amount); - } - - // Reverts - - function test_Unauthorized_Revert() public { - vm.startPrank(STRANGER); - vm.expectRevert(abi.encodeWithSelector(TokenPool.Unauthorized.selector, STRANGER)); - - s_lockReleaseTokenPoolAndProxy.provideLiquidity(1); - } - - function test_Fuzz_ExceedsAllowance(uint256 amount) public { - vm.assume(amount > 0); - vm.expectRevert("ERC20: insufficient allowance"); - s_lockReleaseTokenPoolAndProxy.provideLiquidity(amount); - } - - function test_LiquidityNotAccepted_Revert() public { - s_lockReleaseTokenPoolAndProxy = - new LockReleaseTokenPoolAndProxy(s_token, new address[](0), address(s_mockRMN), false, address(s_sourceRouter)); - - vm.expectRevert(LockReleaseTokenPoolAndProxy.LiquidityNotAccepted.selector); - s_lockReleaseTokenPoolAndProxy.provideLiquidity(1); - } -} - -contract LockReleaseTokenPoolPoolAndProxy_withdrawalLiquidity is LockReleaseTokenPoolAndProxySetup { - function test_Fuzz_WithdrawalLiquidity_Success(uint256 amount) public { - uint256 balancePre = s_token.balanceOf(OWNER); - s_token.approve(address(s_lockReleaseTokenPoolAndProxy), amount); - s_lockReleaseTokenPoolAndProxy.provideLiquidity(amount); - - s_lockReleaseTokenPoolAndProxy.withdrawLiquidity(amount); - - assertEq(s_token.balanceOf(OWNER), balancePre); - } - - // Reverts - - function test_Unauthorized_Revert() public { - vm.startPrank(STRANGER); - vm.expectRevert(abi.encodeWithSelector(TokenPool.Unauthorized.selector, STRANGER)); - - s_lockReleaseTokenPoolAndProxy.withdrawLiquidity(1); - } - - function test_InsufficientLiquidity_Revert() public { - uint256 maxUint256 = 2 ** 256 - 1; - s_token.approve(address(s_lockReleaseTokenPoolAndProxy), maxUint256); - s_lockReleaseTokenPoolAndProxy.provideLiquidity(maxUint256); - - vm.startPrank(address(s_lockReleaseTokenPoolAndProxy)); - s_token.transfer(OWNER, maxUint256); - vm.startPrank(OWNER); - - vm.expectRevert(LockReleaseTokenPoolAndProxy.InsufficientLiquidity.selector); - s_lockReleaseTokenPoolAndProxy.withdrawLiquidity(1); - } -} - -contract LockReleaseTokenPoolPoolAndProxy_supportsInterface is LockReleaseTokenPoolAndProxySetup { - function test_SupportsInterface_Success() public view { - assertTrue(s_lockReleaseTokenPoolAndProxy.supportsInterface(type(IPoolV1).interfaceId)); - assertTrue(s_lockReleaseTokenPoolAndProxy.supportsInterface(type(IERC165).interfaceId)); - } -} diff --git a/contracts/src/v0.8/ccip/test/mocks/MockCommitStore.sol b/contracts/src/v0.8/ccip/test/mocks/MockCommitStore.sol deleted file mode 100644 index aff06016fa5..00000000000 --- a/contracts/src/v0.8/ccip/test/mocks/MockCommitStore.sol +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {ICommitStore} from "../../interfaces/ICommitStore.sol"; - -contract MockCommitStore is ICommitStore { - error PausedError(); - - uint64 private s_expectedNextSequenceNumber = 1; - - bool private s_paused = false; - - /// @inheritdoc ICommitStore - function verify( - bytes32[] calldata, - bytes32[] calldata, - uint256 - ) external view whenNotPaused returns (uint256 timestamp) { - return 1; - } - - function getExpectedNextSequenceNumber() external view returns (uint64) { - return s_expectedNextSequenceNumber; - } - - function setExpectedNextSequenceNumber(uint64 nextSeqNum) external { - s_expectedNextSequenceNumber = nextSeqNum; - } - - modifier whenNotPaused() { - if (paused()) revert PausedError(); - _; - } - - function paused() public view returns (bool) { - return s_paused; - } - - function pause() external { - s_paused = true; - } -} diff --git a/contracts/src/v0.8/ccip/test/mocks/MockE2EUSDCTokenMessenger.sol b/contracts/src/v0.8/ccip/test/mocks/MockE2EUSDCTokenMessenger.sol index 1b6c9c750dd..c142cb89477 100644 --- a/contracts/src/v0.8/ccip/test/mocks/MockE2EUSDCTokenMessenger.sol +++ b/contracts/src/v0.8/ccip/test/mocks/MockE2EUSDCTokenMessenger.sol @@ -21,6 +21,7 @@ import {IMessageTransmitterWithRelay} from "./interfaces/IMessageTransmitterWith // This contract mocks both the ITokenMessenger and IMessageTransmitter // contracts involved with the Cross Chain Token Protocol. +// solhint-disable contract MockE2EUSDCTokenMessenger is ITokenMessenger { uint32 private immutable i_messageBodyVersion; address private immutable i_transmitter; diff --git a/contracts/src/v0.8/ccip/test/mocks/MockE2EUSDCTransmitter.sol b/contracts/src/v0.8/ccip/test/mocks/MockE2EUSDCTransmitter.sol index bbd9c7dcc66..8993154fb7a 100644 --- a/contracts/src/v0.8/ccip/test/mocks/MockE2EUSDCTransmitter.sol +++ b/contracts/src/v0.8/ccip/test/mocks/MockE2EUSDCTransmitter.sol @@ -19,6 +19,7 @@ import {IMessageTransmitterWithRelay} from "./interfaces/IMessageTransmitterWith import {BurnMintERC677} from "../../../shared/token/ERC677/BurnMintERC677.sol"; +// solhint-disable contract MockE2EUSDCTransmitter is IMessageTransmitterWithRelay { // Indicated whether the receiveMessage() call should succeed. bool public s_shouldSucceed; diff --git a/contracts/src/v0.8/ccip/test/mocks/MockRMN1_0.sol b/contracts/src/v0.8/ccip/test/mocks/MockRMN1_0.sol deleted file mode 100644 index d1a488d5577..00000000000 --- a/contracts/src/v0.8/ccip/test/mocks/MockRMN1_0.sol +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {IRMN} from "../../interfaces/IRMN.sol"; -import {OwnerIsCreator} from "./../../../shared/access/OwnerIsCreator.sol"; - -// Inlined from RMN 1.0 contract. -// solhint-disable gas-struct-packing -contract OldRMN { - struct Voter { - address blessVoteAddr; - address curseVoteAddr; - address curseUnvoteAddr; - uint8 blessWeight; - uint8 curseWeight; - } - - struct Config { - Voter[] voters; - uint16 blessWeightThreshold; - uint16 curseWeightThreshold; - } - - struct VersionedConfig { - Config config; - uint32 configVersion; - uint32 blockNumber; - } - - struct UnvoteToCurseRecord { - address curseVoteAddr; - bytes32 cursesHash; - bool forceUnvote; - } -} - -/// @dev Retained almost as-is from commit 88f285b94c23d0c684d337064758a5edde380fe2 for compatibility with offchain -/// tests and scripts. Internal structs of the RMN 1.0 contract that were depended on have been inlined. -/// @dev This contract should no longer be used for any new tests or scripts. -/// @notice WARNING: This contract is to be only used for testing, all methods are unprotected. -// TODO: remove this contract when tests and scripts are updated -contract MockRMN is IRMN, OwnerIsCreator { - error CustomError(bytes err); - - bool private s_curse; - bytes private s_err; - OldRMN.VersionedConfig private s_versionedConfig; - mapping(bytes16 subject => bool cursed) private s_curseBySubject; - - function isCursed() external view override returns (bool) { - if (s_err.length != 0) { - revert CustomError(s_err); - } - return s_curse; - } - - function isCursed(bytes16 subject) external view override returns (bool) { - if (s_err.length != 0) { - revert CustomError(s_err); - } - return s_curse || s_curseBySubject[subject]; - } - - function voteToCurse(bytes32) external { - s_curse = true; - } - - function voteToCurse(bytes32, bytes16 subject) external { - s_curseBySubject[subject] = true; - } - - function ownerUnvoteToCurse(OldRMN.UnvoteToCurseRecord[] memory) external { - s_curse = false; - } - - function ownerUnvoteToCurse(OldRMN.UnvoteToCurseRecord[] memory, bytes16 subject) external { - s_curseBySubject[subject] = false; - } - - function setRevert(bytes memory err) external { - s_err = err; - } - - function isBlessed(IRMN.TaggedRoot calldata) external view override returns (bool) { - return !s_curse; - } - - function getConfigDetails() external view returns (uint32 version, uint32 blockNumber, OldRMN.Config memory config) { - return (s_versionedConfig.configVersion, s_versionedConfig.blockNumber, s_versionedConfig.config); - } -} diff --git a/contracts/src/v0.8/ccip/test/ocr/OCR2Base.t.sol b/contracts/src/v0.8/ccip/test/ocr/OCR2Base.t.sol deleted file mode 100644 index 7511ebdffae..00000000000 --- a/contracts/src/v0.8/ccip/test/ocr/OCR2Base.t.sol +++ /dev/null @@ -1,305 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {OCR2Abstract} from "../../ocr/OCR2Abstract.sol"; -import {OCR2Base} from "../../ocr/OCR2Base.sol"; -import {OCR2Helper} from "../helpers/OCR2Helper.sol"; -import {OCR2Setup} from "./OCR2Setup.t.sol"; - -contract OCR2BaseSetup is OCR2Setup { - OCR2Helper internal s_OCR2Base; - - bytes32[] internal s_rs; - bytes32[] internal s_ss; - bytes32 internal s_rawVs; - - uint40 internal s_latestEpochAndRound; - - function setUp() public virtual override { - OCR2Setup.setUp(); - s_OCR2Base = new OCR2Helper(); - - bytes32 testReportDigest = getTestReportDigest(); - - bytes32[] memory rs = new bytes32[](2); - bytes32[] memory ss = new bytes32[](2); - uint8[] memory vs = new uint8[](2); - - // Calculate signatures - (vs[0], rs[0], ss[0]) = vm.sign(PRIVATE0, testReportDigest); - (vs[1], rs[1], ss[1]) = vm.sign(PRIVATE1, testReportDigest); - - s_rs = rs; - s_ss = ss; - s_rawVs = bytes32(bytes1(vs[0] - 27)) | (bytes32(bytes1(vs[1] - 27)) >> 8); - } - - function getBasicConfigDigest(uint8 f, uint64 currentConfigCount) internal view returns (bytes32) { - bytes memory configBytes = abi.encode(""); - return s_OCR2Base.configDigestFromConfigData( - block.chainid, - address(s_OCR2Base), - currentConfigCount + 1, - s_valid_signers, - s_valid_transmitters, - f, - configBytes, - s_offchainConfigVersion, - configBytes - ); - } - - function getTestReportDigest() internal view returns (bytes32) { - bytes32 configDigest = getBasicConfigDigest(s_f, 0); - bytes32[3] memory reportContext = [configDigest, configDigest, configDigest]; - return keccak256(abi.encodePacked(keccak256(REPORT), reportContext)); - } - - function getBasicConfigDigest( - address contractAddress, - uint8 f, - uint64 currentConfigCount, - bytes memory onchainConfig - ) internal view returns (bytes32) { - return s_OCR2Base.configDigestFromConfigData( - block.chainid, - contractAddress, - currentConfigCount + 1, - s_valid_signers, - s_valid_transmitters, - f, - onchainConfig, - s_offchainConfigVersion, - abi.encode("") - ); - } -} - -contract OCR2Base_transmit is OCR2BaseSetup { - bytes32 internal s_configDigest; - - function setUp() public virtual override { - OCR2BaseSetup.setUp(); - bytes memory configBytes = abi.encode(""); - - s_configDigest = getBasicConfigDigest(s_f, 0); - s_OCR2Base.setOCR2Config( - s_valid_signers, s_valid_transmitters, s_f, configBytes, s_offchainConfigVersion, configBytes - ); - } - - function test_Transmit2SignersSuccess_gas() public { - vm.pauseGasMetering(); - bytes32[3] memory reportContext = [s_configDigest, s_configDigest, s_configDigest]; - - vm.startPrank(s_valid_transmitters[0]); - vm.resumeGasMetering(); - s_OCR2Base.transmit(reportContext, REPORT, s_rs, s_ss, s_rawVs); - } - - // Reverts - - function test_ForkedChain_Revert() public { - bytes32[3] memory reportContext = [s_configDigest, s_configDigest, s_configDigest]; - - uint256 chain1 = block.chainid; - uint256 chain2 = chain1 + 1; - vm.chainId(chain2); - vm.expectRevert(abi.encodeWithSelector(OCR2Base.ForkedChain.selector, chain1, chain2)); - vm.startPrank(s_valid_transmitters[0]); - s_OCR2Base.transmit(reportContext, REPORT, s_rs, s_ss, s_rawVs); - } - - function test_WrongNumberOfSignatures_Revert() public { - bytes32[3] memory reportContext = [s_configDigest, s_configDigest, s_configDigest]; - - vm.expectRevert(OCR2Base.WrongNumberOfSignatures.selector); - s_OCR2Base.transmit(reportContext, REPORT, new bytes32[](0), new bytes32[](0), s_rawVs); - } - - function test_ConfigDigestMismatch_Revert() public { - bytes32 configDigest; - bytes32[3] memory reportContext = [configDigest, configDigest, configDigest]; - - vm.expectRevert(abi.encodeWithSelector(OCR2Base.ConfigDigestMismatch.selector, s_configDigest, configDigest)); - s_OCR2Base.transmit(reportContext, REPORT, new bytes32[](0), new bytes32[](0), s_rawVs); - } - - function test_SignatureOutOfRegistration_Revert() public { - bytes32[3] memory reportContext = [s_configDigest, s_configDigest, s_configDigest]; - - bytes32[] memory rs = new bytes32[](2); - bytes32[] memory ss = new bytes32[](1); - - vm.expectRevert(OCR2Base.SignaturesOutOfRegistration.selector); - s_OCR2Base.transmit(reportContext, REPORT, rs, ss, s_rawVs); - } - - function test_UnAuthorizedTransmitter_Revert() public { - bytes32[3] memory reportContext = [s_configDigest, s_configDigest, s_configDigest]; - bytes32[] memory rs = new bytes32[](2); - bytes32[] memory ss = new bytes32[](2); - - vm.expectRevert(OCR2Base.UnauthorizedTransmitter.selector); - s_OCR2Base.transmit(reportContext, REPORT, rs, ss, s_rawVs); - } - - function test_NonUniqueSignature_Revert() public { - bytes32[3] memory reportContext = [s_configDigest, s_configDigest, s_configDigest]; - bytes32[] memory rs = s_rs; - bytes32[] memory ss = s_ss; - - rs[1] = rs[0]; - ss[1] = ss[0]; - // Need to reset the rawVs to be valid - bytes32 rawVs = bytes32(bytes1(uint8(28) - 27)) | (bytes32(bytes1(uint8(28) - 27)) >> 8); - - vm.startPrank(s_valid_transmitters[0]); - vm.expectRevert(OCR2Base.NonUniqueSignatures.selector); - s_OCR2Base.transmit(reportContext, REPORT, rs, ss, rawVs); - } - - function test_UnauthorizedSigner_Revert() public { - bytes32[3] memory reportContext = [s_configDigest, s_configDigest, s_configDigest]; - bytes32[] memory rs = new bytes32[](2); - rs[0] = s_configDigest; - bytes32[] memory ss = rs; - - vm.startPrank(s_valid_transmitters[0]); - vm.expectRevert(OCR2Base.UnauthorizedSigner.selector); - s_OCR2Base.transmit(reportContext, REPORT, rs, ss, s_rawVs); - } -} - -contract OCR2Base_setOCR2Config is OCR2BaseSetup { - function test_SetConfigSuccess_gas() public { - vm.pauseGasMetering(); - bytes memory configBytes = abi.encode(""); - uint32 configCount = 0; - - bytes32 configDigest = getBasicConfigDigest(s_f, configCount++); - - address[] memory transmitters = s_OCR2Base.getTransmitters(); - assertEq(0, transmitters.length); - - vm.expectEmit(); - emit OCR2Abstract.ConfigSet( - 0, - configDigest, - configCount, - s_valid_signers, - s_valid_transmitters, - s_f, - configBytes, - s_offchainConfigVersion, - configBytes - ); - - s_OCR2Base.setOCR2Config( - s_valid_signers, s_valid_transmitters, s_f, configBytes, s_offchainConfigVersion, configBytes - ); - - transmitters = s_OCR2Base.getTransmitters(); - assertEq(s_valid_transmitters, transmitters); - - configDigest = getBasicConfigDigest(s_f, configCount++); - - vm.expectEmit(); - emit OCR2Abstract.ConfigSet( - uint32(block.number), - configDigest, - configCount, - s_valid_signers, - s_valid_transmitters, - s_f, - configBytes, - s_offchainConfigVersion, - configBytes - ); - vm.resumeGasMetering(); - s_OCR2Base.setOCR2Config( - s_valid_signers, s_valid_transmitters, s_f, configBytes, s_offchainConfigVersion, configBytes - ); - } - - // Reverts - function test_RepeatAddress_Revert() public { - address[] memory signers = new address[](10); - signers[0] = address(1245678); - address[] memory transmitters = new address[](10); - transmitters[0] = signers[0]; - - vm.expectRevert( - abi.encodeWithSelector(OCR2Base.InvalidConfig.selector, OCR2Base.InvalidConfigErrorType.REPEATED_ORACLE_ADDRESS) - ); - s_OCR2Base.setOCR2Config(signers, transmitters, 2, abi.encode(""), 100, abi.encode("")); - } - - function test_SingerCannotBeZeroAddress_Revert() public { - uint256 f = 1; - address[] memory signers = new address[](3 * f + 1); - address[] memory transmitters = new address[](3 * f + 1); - for (uint160 i = 0; i < 3 * f + 1; ++i) { - signers[i] = address(i + 1); - transmitters[i] = address(i + 1000); - } - - signers[0] = address(0); - - vm.expectRevert(OCR2Base.OracleCannotBeZeroAddress.selector); - s_OCR2Base.setOCR2Config(signers, transmitters, uint8(f), abi.encode(""), 100, abi.encode("")); - } - - function test_TransmitterCannotBeZeroAddress_Revert() public { - uint256 f = 1; - address[] memory signers = new address[](3 * f + 1); - address[] memory transmitters = new address[](3 * f + 1); - for (uint160 i = 0; i < 3 * f + 1; ++i) { - signers[i] = address(i + 1); - transmitters[i] = address(i + 1000); - } - - transmitters[0] = address(0); - - vm.expectRevert(OCR2Base.OracleCannotBeZeroAddress.selector); - s_OCR2Base.setOCR2Config(signers, transmitters, uint8(f), abi.encode(""), 100, abi.encode("")); - } - - function test_OracleOutOfRegister_Revert() public { - address[] memory signers = new address[](10); - address[] memory transmitters = new address[](0); - - vm.expectRevert( - abi.encodeWithSelector( - OCR2Base.InvalidConfig.selector, OCR2Base.InvalidConfigErrorType.NUM_SIGNERS_NOT_NUM_TRANSMITTERS - ) - ); - s_OCR2Base.setOCR2Config(signers, transmitters, 2, abi.encode(""), 100, abi.encode("")); - } - - function test_FTooHigh_Revert() public { - address[] memory signers = new address[](0); - uint8 f = 1; - - vm.expectRevert(abi.encodeWithSelector(OCR2Base.InvalidConfig.selector, OCR2Base.InvalidConfigErrorType.F_TOO_HIGH)); - s_OCR2Base.setOCR2Config(signers, new address[](0), f, abi.encode(""), 100, abi.encode("")); - } - - function test_FMustBePositive_Revert() public { - uint8 f = 0; - - vm.expectRevert( - abi.encodeWithSelector(OCR2Base.InvalidConfig.selector, OCR2Base.InvalidConfigErrorType.F_MUST_BE_POSITIVE) - ); - s_OCR2Base.setOCR2Config(new address[](0), new address[](0), f, abi.encode(""), 100, abi.encode("")); - } - - function test_TooManySigners_Revert() public { - address[] memory signers = new address[](32); - - vm.expectRevert( - abi.encodeWithSelector(OCR2Base.InvalidConfig.selector, OCR2Base.InvalidConfigErrorType.TOO_MANY_SIGNERS) - ); - s_OCR2Base.setOCR2Config(signers, new address[](0), 0, abi.encode(""), 100, abi.encode("")); - } -} diff --git a/contracts/src/v0.8/ccip/test/ocr/OCR2BaseNoChecks.t.sol b/contracts/src/v0.8/ccip/test/ocr/OCR2BaseNoChecks.t.sol deleted file mode 100644 index fd4cf3fc9e7..00000000000 --- a/contracts/src/v0.8/ccip/test/ocr/OCR2BaseNoChecks.t.sol +++ /dev/null @@ -1,208 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {OCR2BaseNoChecks} from "../../ocr/OCR2BaseNoChecks.sol"; -import {OCR2NoChecksHelper} from "../helpers/OCR2NoChecksHelper.sol"; -import {OCR2Setup} from "./OCR2Setup.t.sol"; - -contract OCR2BaseNoChecksSetup is OCR2Setup { - OCR2NoChecksHelper internal s_OCR2Base; - - bytes32[] internal s_rs; - bytes32[] internal s_ss; - bytes32 internal s_rawVs; - - function setUp() public virtual override { - OCR2Setup.setUp(); - s_OCR2Base = new OCR2NoChecksHelper(); - } - - function getBasicConfigDigest(uint8 f, uint64 currentConfigCount) internal view returns (bytes32) { - bytes memory configBytes = abi.encode(""); - return s_OCR2Base.configDigestFromConfigData( - block.chainid, - address(s_OCR2Base), - currentConfigCount + 1, - s_valid_signers, - s_valid_transmitters, - f, - configBytes, - s_offchainConfigVersion, - configBytes - ); - } -} - -contract OCR2BaseNoChecks_transmit is OCR2BaseNoChecksSetup { - bytes32 internal s_configDigest; - - function setUp() public virtual override { - OCR2BaseNoChecksSetup.setUp(); - bytes memory configBytes = abi.encode(""); - - s_configDigest = getBasicConfigDigest(s_f, 0); - s_OCR2Base.setOCR2Config( - s_valid_signers, s_valid_transmitters, s_f, configBytes, s_offchainConfigVersion, configBytes - ); - } - - function test_TransmitSuccess_gas() public { - vm.pauseGasMetering(); - bytes32[3] memory reportContext = [s_configDigest, s_configDigest, s_configDigest]; - - vm.startPrank(s_valid_transmitters[0]); - vm.resumeGasMetering(); - s_OCR2Base.transmit(reportContext, REPORT, s_rs, s_ss, s_rawVs); - } - - // Reverts - - function test_ForkedChain_Revert() public { - bytes32[3] memory reportContext = [s_configDigest, s_configDigest, s_configDigest]; - - uint256 chain1 = block.chainid; - uint256 chain2 = chain1 + 1; - vm.chainId(chain2); - vm.expectRevert(abi.encodeWithSelector(OCR2BaseNoChecks.ForkedChain.selector, chain1, chain2)); - vm.startPrank(s_valid_transmitters[0]); - s_OCR2Base.transmit(reportContext, REPORT, s_rs, s_ss, s_rawVs); - } - - function test_ConfigDigestMismatch_Revert() public { - bytes32 configDigest; - - bytes32[3] memory reportContext = [configDigest, configDigest, configDigest]; - - vm.expectRevert( - abi.encodeWithSelector(OCR2BaseNoChecks.ConfigDigestMismatch.selector, s_configDigest, configDigest) - ); - s_OCR2Base.transmit(reportContext, REPORT, new bytes32[](0), new bytes32[](0), s_rawVs); - } - - function test_UnAuthorizedTransmitter_Revert() public { - bytes32[3] memory reportContext = [s_configDigest, s_configDigest, s_configDigest]; - bytes32[] memory rs = new bytes32[](3); - bytes32[] memory ss = new bytes32[](3); - - vm.expectRevert(OCR2BaseNoChecks.UnauthorizedTransmitter.selector); - s_OCR2Base.transmit(reportContext, REPORT, rs, ss, s_rawVs); - } -} - -contract OCR2BaseNoChecks_setOCR2Config is OCR2BaseNoChecksSetup { - event ConfigSet( - uint32 previousConfigBlockNumber, - bytes32 configDigest, - uint64 configCount, - address[] signers, - address[] transmitters, - uint8 f, - bytes onchainConfig, - uint64 offchainConfigVersion, - bytes offchainConfig - ); - - function test_SetConfigSuccess_gas() public { - vm.pauseGasMetering(); - bytes memory configBytes = abi.encode(""); - uint32 configCount = 0; - - bytes32 configDigest = getBasicConfigDigest(s_f, configCount++); - - address[] memory transmitters = s_OCR2Base.getTransmitters(); - assertEq(0, transmitters.length); - - vm.expectEmit(); - emit ConfigSet( - 0, - configDigest, - configCount, - s_valid_signers, - s_valid_transmitters, - s_f, - configBytes, - s_offchainConfigVersion, - configBytes - ); - - s_OCR2Base.setOCR2Config( - s_valid_signers, s_valid_transmitters, s_f, configBytes, s_offchainConfigVersion, configBytes - ); - - transmitters = s_OCR2Base.getTransmitters(); - assertEq(s_valid_transmitters, transmitters); - - configDigest = getBasicConfigDigest(s_f, configCount++); - - vm.expectEmit(); - emit ConfigSet( - uint32(block.number), - configDigest, - configCount, - s_valid_signers, - s_valid_transmitters, - s_f, - configBytes, - s_offchainConfigVersion, - configBytes - ); - vm.resumeGasMetering(); - s_OCR2Base.setOCR2Config( - s_valid_signers, s_valid_transmitters, s_f, configBytes, s_offchainConfigVersion, configBytes - ); - } - - // Reverts - function test_RepeatAddress_Revert() public { - address[] memory signers = new address[](4); - address[] memory transmitters = new address[](4); - transmitters[0] = address(1245678); - transmitters[1] = address(1245678); - transmitters[2] = address(1245678); - transmitters[3] = address(1245678); - - vm.expectRevert( - abi.encodeWithSelector( - OCR2BaseNoChecks.InvalidConfig.selector, OCR2BaseNoChecks.InvalidConfigErrorType.REPEATED_ORACLE_ADDRESS - ) - ); - s_OCR2Base.setOCR2Config(signers, transmitters, 1, abi.encode(""), 100, abi.encode("")); - } - - function test_FMustBePositive_Revert() public { - uint8 f = 0; - - vm.expectRevert( - abi.encodeWithSelector( - OCR2BaseNoChecks.InvalidConfig.selector, OCR2BaseNoChecks.InvalidConfigErrorType.F_MUST_BE_POSITIVE - ) - ); - s_OCR2Base.setOCR2Config(new address[](0), new address[](0), f, abi.encode(""), 100, abi.encode("")); - } - - function test_TransmitterCannotBeZeroAddress_Revert() public { - uint256 f = 1; - address[] memory signers = new address[](3 * f + 1); - address[] memory transmitters = new address[](3 * f + 1); - for (uint160 i = 0; i < 3 * f + 1; ++i) { - signers[i] = address(i + 1); - transmitters[i] = address(i + 1000); - } - - transmitters[0] = address(0); - - vm.expectRevert(OCR2BaseNoChecks.OracleCannotBeZeroAddress.selector); - s_OCR2Base.setOCR2Config(signers, transmitters, uint8(f), abi.encode(""), 100, abi.encode("")); - } - - function test_TooManyTransmitter_Revert() public { - address[] memory transmitters = new address[](100); - - vm.expectRevert( - abi.encodeWithSelector( - OCR2BaseNoChecks.InvalidConfig.selector, OCR2BaseNoChecks.InvalidConfigErrorType.TOO_MANY_TRANSMITTERS - ) - ); - s_OCR2Base.setOCR2Config(new address[](0), transmitters, 0, abi.encode(""), 100, abi.encode("")); - } -} diff --git a/contracts/src/v0.8/ccip/test/ocr/OCR2Setup.t.sol b/contracts/src/v0.8/ccip/test/ocr/OCR2Setup.t.sol deleted file mode 100644 index e4be8ffa29b..00000000000 --- a/contracts/src/v0.8/ccip/test/ocr/OCR2Setup.t.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {Test} from "forge-std/Test.sol"; - -contract OCR2Setup is Test { - uint256 internal constant PRIVATE0 = 0x7b2e97fe057e6de99d6872a2ef2abf52c9b4469bc848c2465ac3fcd8d336e81d; - uint256 internal constant PRIVATE1 = 0xab56160806b05ef1796789248e1d7f34a6465c5280899159d645218cd216cee6; - uint256 internal constant PRIVATE2 = 0x6ec7caa8406a49b76736602810e0a2871959fbbb675e23a8590839e4717f1f7f; - uint256 internal constant PRIVATE3 = 0x80f14b11da94ae7f29d9a7713ea13dc838e31960a5c0f2baf45ed458947b730a; - - address[] internal s_valid_signers; - address[] internal s_valid_transmitters; - - uint64 internal constant s_offchainConfigVersion = 3; - uint8 internal constant s_f = 1; - bytes internal constant REPORT = abi.encode("testReport"); - - function setUp() public virtual { - s_valid_transmitters = new address[](4); - for (uint160 i = 0; i < 4; ++i) { - s_valid_transmitters[i] = address(4 + i); - } - - s_valid_signers = new address[](4); - s_valid_signers[0] = vm.addr(PRIVATE0); //0xc110458BE52CaA6bB68E66969C3218A4D9Db0211 - s_valid_signers[1] = vm.addr(PRIVATE1); //0xc110a19c08f1da7F5FfB281dc93630923F8E3719 - s_valid_signers[2] = vm.addr(PRIVATE2); //0xc110fdF6e8fD679C7Cc11602d1cd829211A18e9b - s_valid_signers[3] = vm.addr(PRIVATE3); //0xc11028017c9b445B6bF8aE7da951B5cC28B326C0 - } -} diff --git a/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMOffRamp.t.sol b/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMOffRamp.t.sol deleted file mode 100644 index 493d02c7c21..00000000000 --- a/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMOffRamp.t.sol +++ /dev/null @@ -1,2235 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {ICommitStore} from "../../interfaces/ICommitStore.sol"; -import {ITokenAdminRegistry} from "../../interfaces/ITokenAdminRegistry.sol"; - -import {CallWithExactGas} from "../../../shared/call/CallWithExactGas.sol"; - -import {GenericReceiver} from "../../../shared/test/testhelpers/GenericReceiver.sol"; -import {AggregateRateLimiter} from "../../AggregateRateLimiter.sol"; -import {Router} from "../../Router.sol"; -import {Client} from "../../libraries/Client.sol"; -import {Internal} from "../../libraries/Internal.sol"; -import {Pool} from "../../libraries/Pool.sol"; -import {RateLimiter} from "../../libraries/RateLimiter.sol"; -import {OCR2Abstract} from "../../ocr/OCR2Abstract.sol"; -import {EVM2EVMOffRamp} from "../../offRamp/EVM2EVMOffRamp.sol"; -import {LockReleaseTokenPool} from "../../pools/LockReleaseTokenPool.sol"; -import {TokenPool} from "../../pools/TokenPool.sol"; -import {EVM2EVMOffRampHelper} from "../helpers/EVM2EVMOffRampHelper.sol"; -import {MaybeRevertingBurnMintTokenPool} from "../helpers/MaybeRevertingBurnMintTokenPool.sol"; -import {ConformingReceiver} from "../helpers/receivers/ConformingReceiver.sol"; -import {MaybeRevertMessageReceiver} from "../helpers/receivers/MaybeRevertMessageReceiver.sol"; -import {MaybeRevertMessageReceiverNo165} from "../helpers/receivers/MaybeRevertMessageReceiverNo165.sol"; -import {ReentrancyAbuser} from "../helpers/receivers/ReentrancyAbuser.sol"; -import {OCR2BaseNoChecks} from "../ocr/OCR2BaseNoChecks.t.sol"; -import {EVM2EVMOffRampSetup} from "./EVM2EVMOffRampSetup.t.sol"; -import {stdError} from "forge-std/Test.sol"; - -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; - -contract EVM2EVMOffRamp_constructor is EVM2EVMOffRampSetup { - function test_Constructor_Success() public { - EVM2EVMOffRamp.StaticConfig memory staticConfig = EVM2EVMOffRamp.StaticConfig({ - commitStore: address(s_mockCommitStore), - chainSelector: DEST_CHAIN_SELECTOR, - sourceChainSelector: SOURCE_CHAIN_SELECTOR, - onRamp: ON_RAMP_ADDRESS, - prevOffRamp: address(0), - rmnProxy: address(s_mockRMN), - tokenAdminRegistry: address(s_tokenAdminRegistry) - }); - EVM2EVMOffRamp.DynamicConfig memory dynamicConfig = - generateDynamicOffRampConfig(address(s_destRouter), address(s_feeQuoter)); - - s_offRamp = new EVM2EVMOffRampHelper(staticConfig, _getInboundRateLimiterConfig()); - - s_offRamp.setOCR2Config( - s_valid_signers, s_valid_transmitters, s_f, abi.encode(dynamicConfig), s_offchainConfigVersion, abi.encode("") - ); - - // Static config - EVM2EVMOffRamp.StaticConfig memory gotStaticConfig = s_offRamp.getStaticConfig(); - assertEq(staticConfig.commitStore, gotStaticConfig.commitStore); - assertEq(staticConfig.sourceChainSelector, gotStaticConfig.sourceChainSelector); - assertEq(staticConfig.chainSelector, gotStaticConfig.chainSelector); - assertEq(staticConfig.onRamp, gotStaticConfig.onRamp); - assertEq(staticConfig.prevOffRamp, gotStaticConfig.prevOffRamp); - assertEq(staticConfig.tokenAdminRegistry, gotStaticConfig.tokenAdminRegistry); - - // Dynamic config - EVM2EVMOffRamp.DynamicConfig memory gotDynamicConfig = s_offRamp.getDynamicConfig(); - _assertSameConfig(dynamicConfig, gotDynamicConfig); - - (uint32 configCount, uint32 blockNumber,) = s_offRamp.latestConfigDetails(); - assertEq(1, configCount); - assertEq(block.number, blockNumber); - - // OffRamp initial values - assertEq("EVM2EVMOffRamp 1.5.0", s_offRamp.typeAndVersion()); - assertEq(OWNER, s_offRamp.owner()); - } - - // Revert - function test_ZeroOnRampAddress_Revert() public { - vm.expectRevert(EVM2EVMOffRamp.ZeroAddressNotAllowed.selector); - - s_offRamp = new EVM2EVMOffRampHelper( - EVM2EVMOffRamp.StaticConfig({ - commitStore: address(s_mockCommitStore), - chainSelector: DEST_CHAIN_SELECTOR, - sourceChainSelector: SOURCE_CHAIN_SELECTOR, - onRamp: ZERO_ADDRESS, - prevOffRamp: address(0), - rmnProxy: address(s_mockRMN), - tokenAdminRegistry: address(s_tokenAdminRegistry) - }), - RateLimiter.Config({isEnabled: true, rate: 1e20, capacity: 1e20}) - ); - } - - function test_CommitStoreAlreadyInUse_Revert() public { - s_mockCommitStore.setExpectedNextSequenceNumber(2); - - vm.expectRevert(EVM2EVMOffRamp.CommitStoreAlreadyInUse.selector); - - s_offRamp = new EVM2EVMOffRampHelper( - EVM2EVMOffRamp.StaticConfig({ - commitStore: address(s_mockCommitStore), - chainSelector: DEST_CHAIN_SELECTOR, - sourceChainSelector: SOURCE_CHAIN_SELECTOR, - onRamp: ON_RAMP_ADDRESS, - prevOffRamp: address(0), - rmnProxy: address(s_mockRMN), - tokenAdminRegistry: address(s_tokenAdminRegistry) - }), - _getInboundRateLimiterConfig() - ); - } -} - -contract EVM2EVMOffRamp_setDynamicConfig is EVM2EVMOffRampSetup { - function test_SetDynamicConfig_Success() public { - EVM2EVMOffRamp.StaticConfig memory staticConfig = s_offRamp.getStaticConfig(); - EVM2EVMOffRamp.DynamicConfig memory dynamicConfig = generateDynamicOffRampConfig(USER_3, address(s_feeQuoter)); - bytes memory onchainConfig = abi.encode(dynamicConfig); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ConfigSet(staticConfig, dynamicConfig); - - vm.expectEmit(); - uint32 configCount = 1; - emit OCR2Abstract.ConfigSet( - uint32(block.number), - getBasicConfigDigest(address(s_offRamp), s_f, configCount, onchainConfig), - configCount + 1, - s_valid_signers, - s_valid_transmitters, - s_f, - onchainConfig, - s_offchainConfigVersion, - abi.encode("") - ); - - s_offRamp.setOCR2Config( - s_valid_signers, s_valid_transmitters, s_f, onchainConfig, s_offchainConfigVersion, abi.encode("") - ); - - EVM2EVMOffRamp.DynamicConfig memory newConfig = s_offRamp.getDynamicConfig(); - _assertSameConfig(dynamicConfig, newConfig); - } - - function test_NonOwner_Revert() public { - vm.startPrank(STRANGER); - EVM2EVMOffRamp.DynamicConfig memory dynamicConfig = generateDynamicOffRampConfig(USER_3, address(s_feeQuoter)); - - vm.expectRevert("Only callable by owner"); - - s_offRamp.setOCR2Config( - s_valid_signers, s_valid_transmitters, s_f, abi.encode(dynamicConfig), s_offchainConfigVersion, abi.encode("") - ); - } - - function test_RouterZeroAddress_Revert() public { - EVM2EVMOffRamp.DynamicConfig memory dynamicConfig = generateDynamicOffRampConfig(ZERO_ADDRESS, ZERO_ADDRESS); - - vm.expectRevert(EVM2EVMOffRamp.ZeroAddressNotAllowed.selector); - - s_offRamp.setOCR2Config( - s_valid_signers, s_valid_transmitters, s_f, abi.encode(dynamicConfig), s_offchainConfigVersion, abi.encode("") - ); - } -} - -contract EVM2EVMOffRamp_metadataHash is EVM2EVMOffRampSetup { - function test_MetadataHash_Success() public view { - bytes32 h = s_offRamp.metadataHash(); - assertEq( - h, - keccak256( - abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, SOURCE_CHAIN_SELECTOR, DEST_CHAIN_SELECTOR, ON_RAMP_ADDRESS) - ) - ); - } -} - -contract EVM2EVMOffRamp_ccipReceive is EVM2EVMOffRampSetup { - // Reverts - - function test_Reverts() public { - Client.Any2EVMMessage memory message = _convertToGeneralMessage(_generateAny2EVMMessageNoTokens(1)); - vm.expectRevert(); - s_offRamp.ccipReceive(message); - } -} - -contract EVM2EVMOffRamp_execute is EVM2EVMOffRampSetup { - error PausedError(); - - function _generateMsgWithoutTokens( - uint256 gasLimit, - bytes memory messageData - ) internal view returns (Internal.EVM2EVMMessage memory) { - Internal.EVM2EVMMessage memory message = _generateAny2EVMMessageNoTokens(1); - message.gasLimit = gasLimit; - message.data = messageData; - message.messageId = Internal._hash( - message, - keccak256( - abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, SOURCE_CHAIN_SELECTOR, DEST_CHAIN_SELECTOR, ON_RAMP_ADDRESS) - ) - ); - return message; - } - - function test_Fuzz_trialExecuteWithoutTokens_Success(bytes4 funcSelector, bytes memory messageData) public { - vm.assume( - funcSelector != GenericReceiver.setRevert.selector && funcSelector != GenericReceiver.setErr.selector - && funcSelector != 0x5100fc21 && funcSelector != 0x00000000 // s_toRevert(), which is public and therefore has a function selector - ); - - // Convert bytes4 into bytes memory to use in the message - Internal.EVM2EVMMessage memory message = _generateMsgWithoutTokens(GAS_LIMIT, messageData); - - // Convert an Internal.EVM2EVMMessage into a Client.Any2EVMMessage digestable by the client - Client.Any2EVMMessage memory receivedMessage = _convertToGeneralMessage(message); - bytes memory expectedCallData = - abi.encodeWithSelector(MaybeRevertMessageReceiver.ccipReceive.selector, receivedMessage); - - vm.expectCall(address(s_receiver), expectedCallData); - (Internal.MessageExecutionState newState, bytes memory err) = - s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - assertEq(uint256(Internal.MessageExecutionState.SUCCESS), uint256(newState)); - assertEq("", err); - } - - function test_Fuzz_trialExecuteWithTokens_Success(uint16 tokenAmount, bytes calldata messageData) public { - vm.assume(tokenAmount != 0); - - uint256[] memory amounts = new uint256[](2); - amounts[0] = uint256(tokenAmount); - amounts[1] = uint256(tokenAmount); - - Internal.EVM2EVMMessage memory message = _generateAny2EVMMessageWithTokens(1, amounts); - message.data = messageData; - - IERC20 dstToken0 = IERC20(s_destTokens[0]); - uint256 startingBalance = dstToken0.balanceOf(message.receiver); - - vm.expectCall(address(dstToken0), abi.encodeWithSelector(IERC20.transfer.selector, address(s_receiver), amounts[0])); - - (Internal.MessageExecutionState newState, bytes memory err) = - s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - assertEq(uint256(Internal.MessageExecutionState.SUCCESS), uint256(newState)); - assertEq("", err); - - // Check that the tokens were transferred - assertEq(startingBalance + amounts[0], dstToken0.balanceOf(message.receiver)); - } - - function test_Fuzz_getSenderNonce(uint8 trialExecutions) public { - vm.assume(trialExecutions > 1); - - Internal.EVM2EVMMessage[] memory messages; - - if (trialExecutions == 1) { - messages = new Internal.EVM2EVMMessage[](1); - messages[0] = _generateAny2EVMMessageNoTokens(0); - } else { - messages = _generateSingleBasicMessage(); - } - - // Fuzz the number of calls from the sender to ensure that getSenderNonce works - for (uint256 i = 1; i < trialExecutions; ++i) { - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - - messages[0].nonce++; - messages[0].sequenceNumber++; - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - } - - messages[0].nonce = 0; - messages[0].sequenceNumber = 0; - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - - uint64 nonceBefore = s_offRamp.getSenderNonce(messages[0].sender); - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - assertEq(s_offRamp.getSenderNonce(messages[0].sender), nonceBefore, "sender nonce is not as expected"); - } - - function test_Fuzz_getSenderNonceWithPrevOffRamp_Success(uint8 trialExecutions) public { - vm.assume(trialExecutions > 1); - // Fuzz a random nonce for getSenderNonce - test_Fuzz_getSenderNonce(trialExecutions); - - address prevOffRamp = address(s_offRamp); - deployOffRamp(s_mockCommitStore, s_destRouter, prevOffRamp); - - // Make sure the off-ramp address has changed by querying the static config - assertNotEq(address(s_offRamp), prevOffRamp); - EVM2EVMOffRamp.StaticConfig memory staticConfig = s_offRamp.getStaticConfig(); - assertEq(staticConfig.prevOffRamp, prevOffRamp, "Previous offRamp does not match expected address"); - - // Since i_prevOffRamp != address(0) and senderNonce == 0, there should be a call to the previous offRamp - vm.expectCall(prevOffRamp, abi.encodeWithSelector(s_offRamp.getSenderNonce.selector, OWNER)); - uint256 currentSenderNonce = s_offRamp.getSenderNonce(OWNER); - assertNotEq(currentSenderNonce, 0, "Sender nonce should not be zero"); - assertEq(currentSenderNonce, trialExecutions - 1, "Sender Nonce does not match expected trial executions"); - - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - - currentSenderNonce = s_offRamp.getSenderNonce(OWNER); - assertEq(currentSenderNonce, trialExecutions - 1, "Sender Nonce on new offramp does not match expected executions"); - } - - function test_SingleMessageNoTokens_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - - messages[0].nonce++; - messages[0].sequenceNumber++; - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - uint64 nonceBefore = s_offRamp.getSenderNonce(messages[0].sender); - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - assertGt(s_offRamp.getSenderNonce(messages[0].sender), nonceBefore); - } - - function test_SingleMessageNoTokensUnordered_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - messages[0].nonce = 0; - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - // Nonce never increments on unordered messages. - uint64 nonceBefore = s_offRamp.getSenderNonce(messages[0].sender); - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - assertEq( - s_offRamp.getSenderNonce(messages[0].sender), nonceBefore, "nonce must remain unchanged on unordered messages" - ); - - messages[0].sequenceNumber++; - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - // Nonce never increments on unordered messages. - nonceBefore = s_offRamp.getSenderNonce(messages[0].sender); - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - assertEq( - s_offRamp.getSenderNonce(messages[0].sender), nonceBefore, "nonce must remain unchanged on unordered messages" - ); - } - - function test_ReceiverError_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - - bytes memory realError1 = new bytes(2); - realError1[0] = 0xbe; - realError1[1] = 0xef; - s_reverting_receiver.setErr(realError1); - - messages[0].receiver = address(s_reverting_receiver); - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, - messages[0].messageId, - Internal.MessageExecutionState.FAILURE, - abi.encodeWithSelector( - EVM2EVMOffRamp.ReceiverError.selector, - abi.encodeWithSelector(MaybeRevertMessageReceiver.CustomError.selector, realError1) - ) - ); - // Nonce should increment on non-strict - assertEq(uint64(0), s_offRamp.getSenderNonce(address(OWNER))); - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - assertEq(uint64(1), s_offRamp.getSenderNonce(address(OWNER))); - } - - function test_StrictUntouchedToSuccess_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - - messages[0].strict = true; - messages[0].receiver = address(s_receiver); - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - // Nonce should increment on a strict untouched -> success. - assertEq(uint64(0), s_offRamp.getSenderNonce(address(OWNER))); - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - assertEq(uint64(1), s_offRamp.getSenderNonce(address(OWNER))); - } - - function test_SkippedIncorrectNonce_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - - messages[0].nonce++; - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - vm.expectEmit(); - emit EVM2EVMOffRamp.SkippedIncorrectNonce(messages[0].nonce, messages[0].sender); - - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - } - - function test_SkippedIncorrectNonceStillExecutes_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateMessagesWithTokens(); - - messages[1].nonce++; - messages[1].messageId = Internal._hash(messages[1], s_offRamp.metadataHash()); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - vm.expectEmit(); - emit EVM2EVMOffRamp.SkippedIncorrectNonce(messages[1].nonce, messages[1].sender); - - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - } - - function test__execute_SkippedAlreadyExecutedMessage_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - - vm.expectEmit(); - emit EVM2EVMOffRamp.SkippedAlreadyExecutedMessage(messages[0].sequenceNumber); - - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - } - - function test__execute_SkippedAlreadyExecutedMessageUnordered_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - messages[0].nonce = 0; - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - - vm.expectEmit(); - emit EVM2EVMOffRamp.SkippedAlreadyExecutedMessage(messages[0].sequenceNumber); - - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - } - - // Send a message to a contract that does not implement the CCIPReceiver interface - // This should execute successfully. - function test_SingleMessageToNonCCIPReceiver_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - MaybeRevertMessageReceiverNo165 newReceiver = new MaybeRevertMessageReceiverNo165(true); - messages[0].receiver = address(newReceiver); - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - } - - function test_SingleMessagesNoTokensSuccess_gas() public { - vm.pauseGasMetering(); - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - Internal.ExecutionReport memory report = _generateReportFromMessages(messages); - - vm.resumeGasMetering(); - s_offRamp.execute(report, new EVM2EVMOffRamp.GasLimitOverride[](0)); - } - - function test_TwoMessagesWithTokensSuccess_gas() public { - vm.pauseGasMetering(); - Internal.EVM2EVMMessage[] memory messages = _generateMessagesWithTokens(); - // Set message 1 to use another receiver to simulate more fair gas costs - messages[1].receiver = address(s_secondary_receiver); - messages[1].messageId = Internal._hash(messages[1], s_offRamp.metadataHash()); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[1].sequenceNumber, messages[1].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - Internal.ExecutionReport memory report = _generateReportFromMessages(messages); - - vm.resumeGasMetering(); - s_offRamp.execute(report, new EVM2EVMOffRamp.GasLimitOverride[](0)); - } - - function test_TwoMessagesWithTokensAndGE_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateMessagesWithTokens(); - // Set message 1 to use another receiver to simulate more fair gas costs - messages[1].receiver = address(s_secondary_receiver); - messages[1].messageId = Internal._hash(messages[1], s_offRamp.metadataHash()); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[1].sequenceNumber, messages[1].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - assertEq(uint64(0), s_offRamp.getSenderNonce(OWNER)); - s_offRamp.execute(_generateReportFromMessages(messages), _getGasLimitsFromMessages(messages)); - assertEq(uint64(2), s_offRamp.getSenderNonce(OWNER)); - } - - function test_Fuzz_InterleavingOrderedAndUnorderedMessages_Success(bool[7] memory orderings) public { - Internal.EVM2EVMMessage[] memory messages = new Internal.EVM2EVMMessage[](orderings.length); - // number of tokens needs to be capped otherwise we hit UnsupportedNumberOfTokens. - Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](3); - for (uint256 i = 0; i < 3; ++i) { - tokenAmounts[i].token = s_sourceTokens[i % s_sourceTokens.length]; - tokenAmounts[i].amount = 1e18; - } - uint64 expectedNonce = 0; - for (uint256 i = 0; i < orderings.length; ++i) { - messages[i] = _generateAny2EVMMessage(uint64(i + 1), tokenAmounts, !orderings[i]); - if (orderings[i]) { - messages[i].nonce = ++expectedNonce; - } - messages[i].messageId = Internal._hash(messages[i], s_offRamp.metadataHash()); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[i].sequenceNumber, messages[i].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - } - - uint64 nonceBefore = s_offRamp.getSenderNonce(OWNER); - assertEq(uint64(0), nonceBefore, "nonce before exec should be 0"); - s_offRamp.execute(_generateReportFromMessages(messages), _getGasLimitsFromMessages(messages)); - // all executions should succeed. - for (uint256 i = 0; i < orderings.length; ++i) { - assertEq( - uint256(s_offRamp.getExecutionState(messages[i].sequenceNumber)), - uint256(Internal.MessageExecutionState.SUCCESS) - ); - } - assertEq(nonceBefore + expectedNonce, s_offRamp.getSenderNonce(OWNER)); - } - - function test_InvalidSourcePoolAddress_Success() public { - address fakePoolAddress = address(0x0000000000333333); - - Internal.EVM2EVMMessage[] memory messages = _generateMessagesWithTokens(); - messages[0].sourceTokenData[0] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(fakePoolAddress), - destTokenAddress: abi.encode(s_destTokenBySourceToken[messages[0].tokenAmounts[0].token]), - extraData: "", - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }) - ); - - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - messages[1].messageId = Internal._hash(messages[1], s_offRamp.metadataHash()); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, - messages[0].messageId, - Internal.MessageExecutionState.FAILURE, - abi.encodeWithSelector( - EVM2EVMOffRamp.TokenHandlingError.selector, - abi.encodeWithSelector(TokenPool.InvalidSourcePoolAddress.selector, abi.encode(fakePoolAddress)) - ) - ); - - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - } - - function test_execute_RouterYULCall_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - - // gas limit too high, Router's external call should revert - messages[0].gasLimit = 1e36; - messages[0].receiver = address(new ConformingReceiver(address(s_destRouter), s_destFeeToken)); - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - Internal.ExecutionReport memory executionReport = _generateReportFromMessages(messages); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, - messages[0].messageId, - Internal.MessageExecutionState.FAILURE, - abi.encodeWithSelector(CallWithExactGas.NotEnoughGasForCall.selector) - ); - - s_offRamp.execute(executionReport, new EVM2EVMOffRamp.GasLimitOverride[](0)); - } - - function test_RetryFailedMessageWithoutManualExecution_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - - bytes memory realError1 = new bytes(2); - realError1[0] = 0xbe; - realError1[1] = 0xef; - s_reverting_receiver.setErr(realError1); - - messages[0].receiver = address(s_reverting_receiver); - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, - messages[0].messageId, - Internal.MessageExecutionState.FAILURE, - abi.encodeWithSelector( - EVM2EVMOffRamp.ReceiverError.selector, - abi.encodeWithSelector(MaybeRevertMessageReceiver.CustomError.selector, realError1) - ) - ); - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - - // The second time should skip the msg - vm.expectEmit(); - emit EVM2EVMOffRamp.AlreadyAttempted(messages[0].sequenceNumber); - - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - } - - // Reverts - - function test_InvalidMessageId_Revert() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - messages[0].nonce++; - // MessageID no longer matches hash. - Internal.ExecutionReport memory executionReport = _generateReportFromMessages(messages); - vm.expectRevert(EVM2EVMOffRamp.InvalidMessageId.selector); - s_offRamp.execute(executionReport, new EVM2EVMOffRamp.GasLimitOverride[](0)); - } - - function test_Paused_Revert() public { - s_mockCommitStore.pause(); - vm.expectRevert(PausedError.selector); - s_offRamp.execute( - _generateReportFromMessages(_generateMessagesWithTokens()), new EVM2EVMOffRamp.GasLimitOverride[](0) - ); - } - - function test_Unhealthy_Revert() public { - s_mockRMN.setGlobalCursed(true); - vm.expectRevert(EVM2EVMOffRamp.CursedByRMN.selector); - s_offRamp.execute( - _generateReportFromMessages(_generateMessagesWithTokens()), new EVM2EVMOffRamp.GasLimitOverride[](0) - ); - // Uncurse should succeed - s_mockRMN.setGlobalCursed(false); - s_offRamp.execute( - _generateReportFromMessages(_generateMessagesWithTokens()), new EVM2EVMOffRamp.GasLimitOverride[](0) - ); - } - - function test_UnexpectedTokenData_Revert() public { - Internal.ExecutionReport memory report = _generateReportFromMessages(_generateSingleBasicMessage()); - report.offchainTokenData = new bytes[][](report.messages.length + 1); - - vm.expectRevert(EVM2EVMOffRamp.UnexpectedTokenData.selector); - - s_offRamp.execute(report, new EVM2EVMOffRamp.GasLimitOverride[](0)); - } - - function test_EmptyReport_Revert() public { - vm.expectRevert(EVM2EVMOffRamp.EmptyReport.selector); - s_offRamp.execute( - Internal.ExecutionReport({ - proofs: new bytes32[](0), - proofFlagBits: 0, - messages: new Internal.EVM2EVMMessage[](0), - offchainTokenData: new bytes[][](0) - }), - new EVM2EVMOffRamp.GasLimitOverride[](0) - ); - } - - function test_RootNotCommitted_Revert() public { - vm.mockCall(address(s_mockCommitStore), abi.encodeWithSelector(ICommitStore.verify.selector), abi.encode(0)); - vm.expectRevert(EVM2EVMOffRamp.RootNotCommitted.selector); - - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - s_offRamp.execute(_generateReportFromMessages(messages), _getGasLimitsFromMessages(messages)); - vm.clearMockedCalls(); - } - - function test_ManualExecutionNotYetEnabled_Revert() public { - vm.mockCall( - address(s_mockCommitStore), abi.encodeWithSelector(ICommitStore.verify.selector), abi.encode(BLOCK_TIME) - ); - vm.expectRevert(EVM2EVMOffRamp.ManualExecutionNotYetEnabled.selector); - - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - s_offRamp.execute(_generateReportFromMessages(messages), _getGasLimitsFromMessages(messages)); - vm.clearMockedCalls(); - } - - function test_InvalidSourceChain_Revert() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - messages[0].sourceChainSelector = SOURCE_CHAIN_SELECTOR + 1; - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOffRamp.InvalidSourceChain.selector, SOURCE_CHAIN_SELECTOR + 1)); - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - } - - function test_UnsupportedNumberOfTokens_Revert() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - Client.EVMTokenAmount[] memory newTokens = new Client.EVMTokenAmount[](MAX_TOKENS_LENGTH + 1); - messages[0].tokenAmounts = newTokens; - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - Internal.ExecutionReport memory report = _generateReportFromMessages(messages); - - vm.expectRevert( - abi.encodeWithSelector(EVM2EVMOffRamp.UnsupportedNumberOfTokens.selector, messages[0].sequenceNumber) - ); - s_offRamp.execute(report, new EVM2EVMOffRamp.GasLimitOverride[](0)); - } - - function test_TokenDataMismatch_Revert() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - Internal.ExecutionReport memory report = _generateReportFromMessages(messages); - - report.offchainTokenData[0] = new bytes[](messages[0].tokenAmounts.length + 1); - - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOffRamp.TokenDataMismatch.selector, messages[0].sequenceNumber)); - s_offRamp.execute(report, new EVM2EVMOffRamp.GasLimitOverride[](0)); - } - - function test_MessageTooLarge_Revert() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - messages[0].data = new bytes(MAX_DATA_SIZE + 1); - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - Internal.ExecutionReport memory executionReport = _generateReportFromMessages(messages); - vm.expectRevert( - abi.encodeWithSelector(EVM2EVMOffRamp.MessageTooLarge.selector, MAX_DATA_SIZE, messages[0].data.length) - ); - s_offRamp.execute(executionReport, new EVM2EVMOffRamp.GasLimitOverride[](0)); - } -} - -contract EVM2EVMOffRamp_execute_upgrade is EVM2EVMOffRampSetup { - EVM2EVMOffRampHelper internal s_prevOffRamp; - - function setUp() public virtual override { - super.setUp(); - - s_prevOffRamp = s_offRamp; - - deployOffRamp(s_mockCommitStore, s_destRouter, address(s_prevOffRamp)); - } - - function test_V2_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - } - - function test_V2SenderNoncesReadsPreviousRamp_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - uint64 startNonce = s_offRamp.getSenderNonce(messages[0].sender); - - for (uint64 i = 1; i < 4; ++i) { - s_prevOffRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - - messages[0].nonce++; - messages[0].sequenceNumber++; - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - assertEq(startNonce + i, s_offRamp.getSenderNonce(messages[0].sender)); - } - } - - function test_V2NonceStartsAtV1Nonce_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - uint64 startNonce = s_offRamp.getSenderNonce(messages[0].sender); - - s_prevOffRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - - assertEq(startNonce + 1, s_offRamp.getSenderNonce(messages[0].sender)); - - messages[0].nonce++; - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - assertEq(startNonce + 2, s_offRamp.getSenderNonce(messages[0].sender)); - - messages[0].nonce++; - messages[0].sequenceNumber++; - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - assertEq(startNonce + 3, s_offRamp.getSenderNonce(messages[0].sender)); - } - - function test_V2NonceNewSenderStartsAtZero_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - s_prevOffRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - - address newSender = address(1234567); - messages[0].sender = newSender; - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - // new sender nonce in new offramp should go from 0 -> 1 - assertEq(s_offRamp.getSenderNonce(newSender), 0); - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - assertEq(s_offRamp.getSenderNonce(newSender), 1); - } - - function test_V2OffRampNonceSkipsIfMsgInFlight_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - - address newSender = address(1234567); - messages[0].sender = newSender; - messages[0].nonce = 2; - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - uint64 startNonce = s_offRamp.getSenderNonce(messages[0].sender); - - // new offramp sees msg nonce higher than senderNonce - // it waits for previous offramp to execute - vm.expectEmit(); - emit EVM2EVMOffRamp.SkippedSenderWithPreviousRampMessageInflight(messages[0].nonce, newSender); - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - assertEq(startNonce, s_offRamp.getSenderNonce(messages[0].sender)); - - messages[0].nonce = 1; - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - // previous offramp executes msg and increases nonce - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - s_prevOffRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - assertEq(startNonce + 1, s_offRamp.getSenderNonce(messages[0].sender)); - - messages[0].nonce = 2; - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - // new offramp is able to execute - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - assertEq(startNonce + 2, s_offRamp.getSenderNonce(messages[0].sender)); - } -} - -contract EVM2EVMOffRamp_executeSingleMessage is EVM2EVMOffRampSetup { - function setUp() public virtual override { - super.setUp(); - vm.startPrank(address(s_offRamp)); - } - - function test_executeSingleMessage_NoTokens_Success() public { - Internal.EVM2EVMMessage memory message = _generateAny2EVMMessageNoTokens(1); - s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - } - - function test_executeSingleMessage_WithTokens_Success() public { - Internal.EVM2EVMMessage memory message = _generateMessagesWithTokens()[0]; - bytes[] memory offchainTokenData = new bytes[](message.tokenAmounts.length); - Internal.SourceTokenData memory sourceTokenData = abi.decode(message.sourceTokenData[0], (Internal.SourceTokenData)); - - vm.expectCall( - s_destPoolByToken[s_destTokens[0]], - abi.encodeWithSelector( - LockReleaseTokenPool.releaseOrMint.selector, - Pool.ReleaseOrMintInV1({ - originalSender: abi.encode(message.sender), - receiver: message.receiver, - amount: message.tokenAmounts[0].amount, - localToken: s_destTokenBySourceToken[message.tokenAmounts[0].token], - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: sourceTokenData.extraData, - offchainTokenData: "" - }) - ) - ); - - s_offRamp.executeSingleMessage(message, offchainTokenData, new uint32[](0)); - } - - function test_executeSingleMessage_ZeroGasZeroData_Success() public { - uint256 gasLimit = 0; - Internal.EVM2EVMMessage memory message = _generateMsgWithoutTokens(gasLimit); - Client.Any2EVMMessage memory receiverMsg = _convertToGeneralMessage(message); - - // expect 0 calls to be made as no gas is provided - vm.expectCall( - address(s_destRouter), - abi.encodeCall(Router.routeMessage, (receiverMsg, Internal.GAS_FOR_CALL_EXACT_CHECK, gasLimit, message.receiver)), - 0 - ); - - s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - - // Ensure we encoded it properly, and didn't simply expect the wrong call - gasLimit = 200_000; - message = _generateMsgWithoutTokens(gasLimit); - receiverMsg = _convertToGeneralMessage(message); - - vm.expectCall( - address(s_destRouter), - abi.encodeCall(Router.routeMessage, (receiverMsg, Internal.GAS_FOR_CALL_EXACT_CHECK, gasLimit, message.receiver)), - 1 - ); - - s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - } - - function _generateMsgWithoutTokens(uint256 gasLimit) internal view returns (Internal.EVM2EVMMessage memory) { - Internal.EVM2EVMMessage memory message = _generateAny2EVMMessageNoTokens(1); - message.gasLimit = gasLimit; - message.data = ""; - message.messageId = Internal._hash( - message, - keccak256( - abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, SOURCE_CHAIN_SELECTOR, DEST_CHAIN_SELECTOR, ON_RAMP_ADDRESS) - ) - ); - return message; - } - - function test_NonContract_Success() public { - Internal.EVM2EVMMessage memory message = _generateAny2EVMMessageNoTokens(1); - message.receiver = STRANGER; - s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - } - - function test_NonContractWithTokens_Success() public { - uint256[] memory amounts = new uint256[](2); - amounts[0] = 1000; - amounts[1] = 50; - vm.expectEmit(); - emit TokenPool.Released(address(s_offRamp), STRANGER, amounts[0]); - vm.expectEmit(); - emit TokenPool.Minted(address(s_offRamp), STRANGER, amounts[1]); - Internal.EVM2EVMMessage memory message = _generateAny2EVMMessageWithTokens(1, amounts); - message.receiver = STRANGER; - s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - } - - // Reverts - - function test_TokenHandlingError_Revert() public { - uint256[] memory amounts = new uint256[](2); - amounts[0] = 1000; - amounts[1] = 50; - - bytes memory errorMessage = "Random token pool issue"; - - Internal.EVM2EVMMessage memory message = _generateAny2EVMMessageWithTokens(1, amounts); - s_maybeRevertingPool.setShouldRevert(errorMessage); - - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOffRamp.TokenHandlingError.selector, errorMessage)); - - s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - } - - function test_ZeroGasDONExecution_Revert() public { - Internal.EVM2EVMMessage memory message = _generateAny2EVMMessageNoTokens(1); - message.gasLimit = 0; - - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOffRamp.ReceiverError.selector, "")); - - s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - } - - function test_MessageSender_Revert() public { - vm.stopPrank(); - Internal.EVM2EVMMessage memory message = _generateAny2EVMMessageNoTokens(1); - vm.expectRevert(EVM2EVMOffRamp.CanOnlySelfCall.selector); - s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - } -} - -contract EVM2EVMOffRamp__report is EVM2EVMOffRampSetup { - // Asserts that execute completes - function test_Report_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - Internal.ExecutionReport memory report = _generateReportFromMessages(messages); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - s_offRamp.report(abi.encode(report)); - } -} - -contract EVM2EVMOffRamp_manuallyExecute is EVM2EVMOffRampSetup { - function test_ManualExec_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - messages[0].receiver = address(s_reverting_receiver); - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - - s_reverting_receiver.setRevert(false); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - s_offRamp.manuallyExecute( - _generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](messages.length) - ); - } - - function test_ManualExecWithSourceTokens_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessageWithTokens(); - messages[0].receiver = address(s_reverting_receiver); - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - s_reverting_receiver.setRevert(false); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - EVM2EVMOffRamp.GasLimitOverride[] memory gasLimitOverrides = _getGasLimitsFromMessages(messages); - s_offRamp.manuallyExecute(_generateReportFromMessages(messages), gasLimitOverrides); - } - - function test_ManualExecWithMultipleMessagesAndSourceTokens_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateMessagesWithTokens(); - messages[0].receiver = address(s_reverting_receiver); - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - messages[1].receiver = address(s_reverting_receiver); - messages[1].messageId = Internal._hash(messages[1], s_offRamp.metadataHash()); - - s_reverting_receiver.setRevert(false); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[1].sequenceNumber, messages[1].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - EVM2EVMOffRamp.GasLimitOverride[] memory gasLimitOverrides = _getGasLimitsFromMessages(messages); - s_offRamp.manuallyExecute(_generateReportFromMessages(messages), gasLimitOverrides); - } - - function test_manuallyExecute_DoesNotRevertIfUntouched_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - messages[0].receiver = address(s_reverting_receiver); - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - assertEq(messages[0].nonce - 1, s_offRamp.getSenderNonce(messages[0].sender)); - - s_reverting_receiver.setRevert(true); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, - messages[0].messageId, - Internal.MessageExecutionState.FAILURE, - abi.encodeWithSelector( - EVM2EVMOffRamp.ReceiverError.selector, - abi.encodeWithSelector(MaybeRevertMessageReceiver.CustomError.selector, "") - ) - ); - - s_offRamp.manuallyExecute( - _generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](messages.length) - ); - - assertEq(messages[0].nonce, s_offRamp.getSenderNonce(messages[0].sender)); - } - - function test_manuallyExecute_WithGasOverride_Success() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - messages[0].receiver = address(s_reverting_receiver); - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](messages.length)); - - s_reverting_receiver.setRevert(false); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - EVM2EVMOffRamp.GasLimitOverride[] memory gasLimitOverrides = _getGasLimitsFromMessages(messages); - gasLimitOverrides[0].receiverExecutionGasLimit += 1; - - s_offRamp.manuallyExecute(_generateReportFromMessages(messages), gasLimitOverrides); - } - - function test_manuallyExecute_WithInvalidSourceTokenDataCount_Revert() public { - uint256 messageIndex = 0; - - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessageWithTokens(); - messages[messageIndex].receiver = address(s_reverting_receiver); - messages[messageIndex].messageId = Internal._hash(messages[messageIndex], s_offRamp.metadataHash()); - - EVM2EVMOffRamp.GasLimitOverride[] memory gasLimitOverrides = _getGasLimitsFromMessages(messages); - - messages[messageIndex].sourceTokenData = new bytes[](0); - - vm.expectRevert(stdError.indexOOBError); - s_offRamp.manuallyExecute(_generateReportFromMessages(messages), gasLimitOverrides); - } - - function test_manuallyExecute_WithInvalidReceiverExecutionGasOverride_Revert() public { - uint256 messageIndex = 0; - - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - messages[messageIndex].receiver = address(s_reverting_receiver); - messages[messageIndex].messageId = Internal._hash(messages[messageIndex], s_offRamp.metadataHash()); - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](messages.length)); - - s_reverting_receiver.setRevert(false); - EVM2EVMOffRamp.GasLimitOverride[] memory gasLimitOverrides = _getGasLimitsFromMessages(messages); - gasLimitOverrides[messageIndex].receiverExecutionGasLimit -= 1; - - vm.expectRevert( - abi.encodeWithSelector( - EVM2EVMOffRamp.InvalidManualExecutionGasLimit.selector, - messages[messageIndex].messageId, - messages[messageIndex].gasLimit, - gasLimitOverrides[messageIndex].receiverExecutionGasLimit - ) - ); - - s_offRamp.manuallyExecute(_generateReportFromMessages(messages), gasLimitOverrides); - } - - function test_manuallyExecute_LowGasLimitManualExec_Success() public { - uint256 messageIndex = 0; - - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - messages[messageIndex].gasLimit = 1; - messages[messageIndex].receiver = address(new ConformingReceiver(address(s_destRouter), s_destFeeToken)); - messages[messageIndex].messageId = Internal._hash(messages[messageIndex], s_offRamp.metadataHash()); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[messageIndex].sequenceNumber, - messages[messageIndex].messageId, - Internal.MessageExecutionState.FAILURE, - abi.encodeWithSelector(EVM2EVMOffRamp.ReceiverError.selector, "") - ); - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - - EVM2EVMOffRamp.GasLimitOverride[] memory gasLimitOverrides = new EVM2EVMOffRamp.GasLimitOverride[](1); - gasLimitOverrides[messageIndex].receiverExecutionGasLimit = 100_000; - - vm.expectEmit(); - emit MaybeRevertMessageReceiver.MessageReceived(); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[messageIndex].sequenceNumber, - messages[messageIndex].messageId, - Internal.MessageExecutionState.SUCCESS, - "" - ); - s_offRamp.manuallyExecute(_generateReportFromMessages(messages), gasLimitOverrides); - } - - function test_ReentrancyManualExecuteFails_Success() public { - uint256 tokenAmount = 1e9; - IERC20 tokenToAbuse = IERC20(s_destFeeToken); - - // This needs to be deployed before the source chain message is sent - // because we need the address for the receiver. - ReentrancyAbuser receiver = new ReentrancyAbuser(address(s_destRouter), s_offRamp); - uint256 balancePre = tokenToAbuse.balanceOf(address(receiver)); - - // For this test any message will be flagged as correct by the - // commitStore. In a real scenario the abuser would have to actually - // send the message that they want to replay. - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - messages[0].tokenAmounts = new Client.EVMTokenAmount[](1); - messages[0].tokenAmounts[0] = Client.EVMTokenAmount({token: s_sourceFeeToken, amount: tokenAmount}); - messages[0].receiver = address(receiver); - messages[0].sourceTokenData = new bytes[](1); - messages[0].sourceTokenData[0] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[s_sourceFeeToken]), - destTokenAddress: abi.encode(s_destTokenBySourceToken[s_sourceFeeToken]), - extraData: "", - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }) - ); - - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - Internal.ExecutionReport memory report = _generateReportFromMessages(messages); - - // sets the report to be repeated on the ReentrancyAbuser to be able to replay - receiver.setPayload(report); - - // The first entry should be fine and triggers the second entry which is skipped. Due to the reentrancy - // the second completes first, so we expect the skip event before the success event. - vm.expectEmit(); - emit EVM2EVMOffRamp.SkippedAlreadyExecutedMessage(messages[0].sequenceNumber); - - vm.expectEmit(); - emit EVM2EVMOffRamp.ExecutionStateChanged( - messages[0].sequenceNumber, messages[0].messageId, Internal.MessageExecutionState.SUCCESS, "" - ); - - EVM2EVMOffRamp.GasLimitOverride[] memory gasLimits = _getGasLimitsFromMessages(messages); - s_offRamp.manuallyExecute(report, gasLimits); - - // Assert that they only got the tokens once, not twice - assertEq(tokenToAbuse.balanceOf(address(receiver)), balancePre + tokenAmount); - } - - function test_manuallyExecute_InvalidTokenGasOverride_Revert() public { - uint256 failingMessageIndex = 0; - uint256 failingTokenIndex = 0; - - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessageWithTokens(); - messages[failingMessageIndex].receiver = address(s_reverting_receiver); - messages[failingMessageIndex].messageId = Internal._hash(messages[failingMessageIndex], s_offRamp.metadataHash()); - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](messages.length)); - - s_reverting_receiver.setRevert(false); - EVM2EVMOffRamp.GasLimitOverride[] memory gasLimitOverrides = _getGasLimitsFromMessages(messages); - gasLimitOverrides[failingMessageIndex].tokenGasOverrides[failingTokenIndex] -= 2; - - vm.expectRevert( - abi.encodeWithSelector( - EVM2EVMOffRamp.InvalidTokenGasOverride.selector, - messages[failingMessageIndex].messageId, - failingTokenIndex, - DEFAULT_TOKEN_DEST_GAS_OVERHEAD, - gasLimitOverrides[failingMessageIndex].tokenGasOverrides[failingTokenIndex] - ) - ); - - s_offRamp.manuallyExecute(_generateReportFromMessages(messages), gasLimitOverrides); - } - - function test_manuallyExecute_DestinationGasAmountCountMismatch_Revert() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessageWithTokens(); - messages[0].receiver = address(s_reverting_receiver); - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](messages.length)); - - s_reverting_receiver.setRevert(false); - EVM2EVMOffRamp.GasLimitOverride[] memory gasLimitOverrides = _getGasLimitsFromMessages(messages); - gasLimitOverrides[0].tokenGasOverrides = new uint32[](0); - - vm.expectRevert( - abi.encodeWithSelector(EVM2EVMOffRamp.DestinationGasAmountCountMismatch.selector, messages[0].messageId, 1) - ); - - s_offRamp.manuallyExecute(_generateReportFromMessages(messages), gasLimitOverrides); - } - - function test_ManualExecForkedChain_Revert() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - - Internal.ExecutionReport memory report = _generateReportFromMessages(messages); - uint256 chain1 = block.chainid; - uint256 chain2 = chain1 + 1; - vm.chainId(chain2); - vm.expectRevert(abi.encodeWithSelector(OCR2BaseNoChecks.ForkedChain.selector, chain1, chain2)); - - s_offRamp.manuallyExecute(report, _getGasLimitsFromMessages(messages)); - } - - function test_ManualExecGasLimitMismatch_Revert() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - - vm.expectRevert(EVM2EVMOffRamp.ManualExecutionGasLimitMismatch.selector); - s_offRamp.manuallyExecute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - - vm.expectRevert(EVM2EVMOffRamp.ManualExecutionGasLimitMismatch.selector); - s_offRamp.manuallyExecute( - _generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](messages.length - 1) - ); - - vm.expectRevert(EVM2EVMOffRamp.ManualExecutionGasLimitMismatch.selector); - s_offRamp.manuallyExecute( - _generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](messages.length + 1) - ); - } - - function test_ManualExecInvalidGasLimit_Revert() public { - uint256 messageIndex = 0; - - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - EVM2EVMOffRamp.GasLimitOverride[] memory gasLimits = _getGasLimitsFromMessages(messages); - gasLimits[messageIndex].receiverExecutionGasLimit -= 1; - - vm.expectRevert( - abi.encodeWithSelector( - EVM2EVMOffRamp.InvalidManualExecutionGasLimit.selector, - messages[messageIndex].messageId, - messages[messageIndex].gasLimit, - gasLimits[messageIndex].receiverExecutionGasLimit - ) - ); - s_offRamp.manuallyExecute(_generateReportFromMessages(messages), gasLimits); - } - - function test_ManualExecFailedTx_Revert() public { - Internal.EVM2EVMMessage[] memory messages = _generateSingleBasicMessage(); - - messages[0].receiver = address(s_reverting_receiver); - messages[0].messageId = Internal._hash(messages[0], s_offRamp.metadataHash()); - - s_offRamp.execute(_generateReportFromMessages(messages), new EVM2EVMOffRamp.GasLimitOverride[](0)); - - s_reverting_receiver.setRevert(true); - - vm.expectRevert( - abi.encodeWithSelector( - EVM2EVMOffRamp.ExecutionError.selector, - abi.encodeWithSelector( - EVM2EVMOffRamp.ReceiverError.selector, - abi.encodeWithSelector(MaybeRevertMessageReceiver.CustomError.selector, bytes("")) - ) - ) - ); - s_offRamp.manuallyExecute(_generateReportFromMessages(messages), _getGasLimitsFromMessages(messages)); - } -} - -contract EVM2EVMOffRamp_getExecutionState is EVM2EVMOffRampSetup { - mapping(uint64 seqNum => Internal.MessageExecutionState state) internal s_differentialExecutionState; - - /// forge-config: default.fuzz.runs = 32 - /// forge-config: ccip.fuzz.runs = 32 - function test_Fuzz_Differential_Success(uint16[500] memory seqNums, uint8[500] memory values) public { - for (uint256 i = 0; i < seqNums.length; ++i) { - // Only use the first three slots. This makes sure existing slots get overwritten - // as the tests uses 500 sequence numbers. - uint16 seqNum = seqNums[i] % 386; - Internal.MessageExecutionState state = Internal.MessageExecutionState(values[i] % 4); - s_differentialExecutionState[seqNum] = state; - s_offRamp.setExecutionStateHelper(seqNum, state); - assertEq(uint256(state), uint256(s_offRamp.getExecutionState(seqNum))); - } - - for (uint256 i = 0; i < seqNums.length; ++i) { - uint16 seqNum = seqNums[i] % 386; - Internal.MessageExecutionState expectedState = s_differentialExecutionState[seqNum]; - assertEq(uint256(expectedState), uint256(s_offRamp.getExecutionState(seqNum))); - } - } - - function test_GetExecutionState_Success() public { - s_offRamp.setExecutionStateHelper(0, Internal.MessageExecutionState.FAILURE); - assertEq(s_offRamp.getExecutionStateBitMap(0), 3); - - s_offRamp.setExecutionStateHelper(1, Internal.MessageExecutionState.FAILURE); - assertEq(s_offRamp.getExecutionStateBitMap(0), 3 + (3 << 2)); - - s_offRamp.setExecutionStateHelper(1, Internal.MessageExecutionState.IN_PROGRESS); - assertEq(s_offRamp.getExecutionStateBitMap(0), 3 + (1 << 2)); - - s_offRamp.setExecutionStateHelper(2, Internal.MessageExecutionState.FAILURE); - assertEq(s_offRamp.getExecutionStateBitMap(0), 3 + (1 << 2) + (3 << 4)); - - s_offRamp.setExecutionStateHelper(127, Internal.MessageExecutionState.IN_PROGRESS); - assertEq(s_offRamp.getExecutionStateBitMap(0), 3 + (1 << 2) + (3 << 4) + (1 << 254)); - - s_offRamp.setExecutionStateHelper(128, Internal.MessageExecutionState.SUCCESS); - assertEq(s_offRamp.getExecutionStateBitMap(0), 3 + (1 << 2) + (3 << 4) + (1 << 254)); - assertEq(s_offRamp.getExecutionStateBitMap(1), 2); - - assertEq(uint256(Internal.MessageExecutionState.FAILURE), uint256(s_offRamp.getExecutionState(0))); - assertEq(uint256(Internal.MessageExecutionState.IN_PROGRESS), uint256(s_offRamp.getExecutionState(1))); - assertEq(uint256(Internal.MessageExecutionState.FAILURE), uint256(s_offRamp.getExecutionState(2))); - assertEq(uint256(Internal.MessageExecutionState.IN_PROGRESS), uint256(s_offRamp.getExecutionState(127))); - assertEq(uint256(Internal.MessageExecutionState.SUCCESS), uint256(s_offRamp.getExecutionState(128))); - } - - function test_FillExecutionState_Success() public { - for (uint64 i = 0; i < 384; ++i) { - s_offRamp.setExecutionStateHelper(i, Internal.MessageExecutionState.FAILURE); - } - - for (uint64 i = 0; i < 384; ++i) { - assertEq(uint256(Internal.MessageExecutionState.FAILURE), uint256(s_offRamp.getExecutionState(i))); - } - - for (uint64 i = 0; i < 3; ++i) { - assertEq(type(uint256).max, s_offRamp.getExecutionStateBitMap(i)); - } - - for (uint64 i = 0; i < 384; ++i) { - s_offRamp.setExecutionStateHelper(i, Internal.MessageExecutionState.IN_PROGRESS); - } - - for (uint64 i = 0; i < 384; ++i) { - assertEq(uint256(Internal.MessageExecutionState.IN_PROGRESS), uint256(s_offRamp.getExecutionState(i))); - } - - for (uint64 i = 0; i < 3; ++i) { - // 0x555... == 0b101010101010..... - assertEq(0x5555555555555555555555555555555555555555555555555555555555555555, s_offRamp.getExecutionStateBitMap(i)); - } - } -} - -contract EVM2EVMOffRamp__trialExecute is EVM2EVMOffRampSetup { - function test_trialExecute_Success() public { - uint256[] memory amounts = new uint256[](2); - amounts[0] = 1000; - amounts[1] = 50; - - Internal.EVM2EVMMessage memory message = _generateAny2EVMMessageWithTokens(1, amounts); - IERC20 dstToken0 = IERC20(s_destTokens[0]); - uint256 startingBalance = dstToken0.balanceOf(message.receiver); - - (Internal.MessageExecutionState newState, bytes memory err) = - s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - assertEq(uint256(Internal.MessageExecutionState.SUCCESS), uint256(newState)); - assertEq("", err); - - // Check that the tokens were transferred - assertEq(startingBalance + amounts[0], dstToken0.balanceOf(message.receiver)); - } - - function test_TokenHandlingErrorIsCaught_Success() public { - uint256[] memory amounts = new uint256[](2); - amounts[0] = 1000; - amounts[1] = 50; - - IERC20 dstToken0 = IERC20(s_destTokens[0]); - uint256 startingBalance = dstToken0.balanceOf(OWNER); - - bytes memory errorMessage = "Random token pool issue"; - - Internal.EVM2EVMMessage memory message = _generateAny2EVMMessageWithTokens(1, amounts); - s_maybeRevertingPool.setShouldRevert(errorMessage); - - (Internal.MessageExecutionState newState, bytes memory err) = - s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - assertEq(uint256(Internal.MessageExecutionState.FAILURE), uint32(newState)); - assertEq(abi.encodeWithSelector(EVM2EVMOffRamp.TokenHandlingError.selector, errorMessage), err); - - // Expect the balance to remain the same - assertEq(startingBalance, dstToken0.balanceOf(OWNER)); - } - - function test_RateLimitError_Success() public { - uint256[] memory amounts = new uint256[](2); - amounts[0] = 1000; - amounts[1] = 50; - - bytes memory errorMessage = abi.encodeWithSelector(RateLimiter.BucketOverfilled.selector); - - Internal.EVM2EVMMessage memory message = _generateAny2EVMMessageWithTokens(1, amounts); - s_maybeRevertingPool.setShouldRevert(errorMessage); - - (Internal.MessageExecutionState newState, bytes memory err) = - s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - assertEq(uint256(Internal.MessageExecutionState.FAILURE), uint256(newState)); - assertEq(abi.encodeWithSelector(EVM2EVMOffRamp.TokenHandlingError.selector, errorMessage), err); - } - - function test_TokenPoolIsNotAContract_Success() public { - uint256[] memory amounts = new uint256[](2); - amounts[0] = 10000; - Internal.EVM2EVMMessage memory message = _generateAny2EVMMessageWithTokens(1, amounts); - - // Happy path, pool is correct - (Internal.MessageExecutionState newState, bytes memory err) = - s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - - assertEq(uint256(Internal.MessageExecutionState.SUCCESS), uint256(newState)); - assertEq("", err); - - // address 0 has no contract - assertEq(address(0).code.length, 0); - message.sourceTokenData[0] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(address(0)), - destTokenAddress: abi.encode(address(0)), - extraData: "", - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }) - ); - - message.messageId = Internal._hash( - message, - keccak256( - abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, SOURCE_CHAIN_SELECTOR, DEST_CHAIN_SELECTOR, ON_RAMP_ADDRESS) - ) - ); - - // Unhappy path, no revert but marked as failed. - (newState, err) = s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - - assertEq(uint256(Internal.MessageExecutionState.FAILURE), uint256(newState)); - assertEq(abi.encodeWithSelector(Internal.InvalidEVMAddress.selector, abi.encode(address(0))), err); - - address notAContract = makeAddr("not_a_contract"); - - message.sourceTokenData[0] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(address(0)), - destTokenAddress: abi.encode(notAContract), - extraData: "", - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }) - ); - - message.messageId = Internal._hash( - message, - keccak256( - abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, SOURCE_CHAIN_SELECTOR, DEST_CHAIN_SELECTOR, ON_RAMP_ADDRESS) - ) - ); - - (newState, err) = s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - - assertEq(uint256(Internal.MessageExecutionState.FAILURE), uint256(newState)); - assertEq(abi.encodeWithSelector(EVM2EVMOffRamp.NotACompatiblePool.selector, address(0)), err); - } -} - -contract EVM2EVMOffRamp__releaseOrMintToken is EVM2EVMOffRampSetup { - function test__releaseOrMintToken_Success() public { - uint256 amount = 123123; - address token = s_sourceTokens[0]; - bytes memory originalSender = abi.encode(OWNER); - bytes memory offchainTokenData = ""; - - IERC20 dstToken1 = IERC20(s_destTokenBySourceToken[token]); - uint256 startingBalance = dstToken1.balanceOf(OWNER); - - Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), - destTokenAddress: abi.encode(s_destTokenBySourceToken[token]), - extraData: "", - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }); - - vm.expectCall( - s_destPoolBySourceToken[token], - abi.encodeWithSelector( - LockReleaseTokenPool.releaseOrMint.selector, - Pool.ReleaseOrMintInV1({ - originalSender: originalSender, - receiver: OWNER, - amount: amount, - localToken: s_destTokenBySourceToken[token], - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: sourceTokenData.extraData, - offchainTokenData: offchainTokenData - }) - ) - ); - - s_offRamp.releaseOrMintToken(amount, originalSender, OWNER, sourceTokenData, offchainTokenData); - - assertEq(startingBalance + amount, dstToken1.balanceOf(OWNER)); - } - - function test_releaseOrMintToken_InvalidDataLength_Revert() public { - uint256 amount = 123123; - address token = s_sourceTokens[0]; - - Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), - destTokenAddress: abi.encode(s_destTokenBySourceToken[token]), - extraData: "", - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }); - - // Mock the call so returns 2 slots of data - vm.mockCall( - s_destTokenBySourceToken[token], abi.encodeWithSelector(IERC20.balanceOf.selector, OWNER), abi.encode(0, 0) - ); - - vm.expectRevert( - abi.encodeWithSelector(EVM2EVMOffRamp.InvalidDataLength.selector, Internal.MAX_BALANCE_OF_RET_BYTES, 64) - ); - - s_offRamp.releaseOrMintToken(amount, abi.encode(OWNER), OWNER, sourceTokenData, ""); - } - - function test_releaseOrMintToken_TokenHandlingError_BalanceOf_Revert() public { - uint256 amount = 123123; - address token = s_sourceTokens[0]; - - Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), - destTokenAddress: abi.encode(s_destTokenBySourceToken[token]), - extraData: "", - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }); - - bytes memory revertData = "failed to balanceOf"; - - // Mock the call so returns 2 slots of data - vm.mockCallRevert( - s_destTokenBySourceToken[token], abi.encodeWithSelector(IERC20.balanceOf.selector, OWNER), revertData - ); - - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOffRamp.TokenHandlingError.selector, revertData)); - - s_offRamp.releaseOrMintToken(amount, abi.encode(OWNER), OWNER, sourceTokenData, ""); - } - - function test_releaseOrMintToken_ReleaseOrMintBalanceMismatch_Revert() public { - uint256 amount = 123123; - address token = s_sourceTokens[0]; - uint256 mockedStaticBalance = 50000; - - Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), - destTokenAddress: abi.encode(s_destTokenBySourceToken[token]), - extraData: "", - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }); - - vm.mockCall( - s_destTokenBySourceToken[token], - abi.encodeWithSelector(IERC20.balanceOf.selector, OWNER), - abi.encode(mockedStaticBalance) - ); - - vm.expectRevert( - abi.encodeWithSelector( - EVM2EVMOffRamp.ReleaseOrMintBalanceMismatch.selector, amount, mockedStaticBalance, mockedStaticBalance - ) - ); - - s_offRamp.releaseOrMintToken(amount, abi.encode(OWNER), OWNER, sourceTokenData, ""); - } - - function test_releaseOrMintToken_skip_ReleaseOrMintBalanceMismatch_if_pool_Revert() public { - uint256 amount = 123123; - address token = s_sourceTokens[0]; - uint256 mockedStaticBalance = 50000; - - Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), - destTokenAddress: abi.encode(s_destTokenBySourceToken[token]), - extraData: "", - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }); - - // This should make the call fail if it does not skip the check - vm.mockCall( - s_destTokenBySourceToken[token], - abi.encodeWithSelector(IERC20.balanceOf.selector, OWNER), - abi.encode(mockedStaticBalance) - ); - - s_offRamp.releaseOrMintToken(amount, abi.encode(OWNER), s_destPoolBySourceToken[token], sourceTokenData, ""); - } - - function test__releaseOrMintToken_NotACompatiblePool_Revert() public { - uint256 amount = 123123; - address token = s_sourceTokens[0]; - address destToken = s_destTokenBySourceToken[token]; - vm.label(destToken, "destToken"); - bytes memory originalSender = abi.encode(OWNER); - - Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), - destTokenAddress: abi.encode(destToken), - extraData: "", - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }); - - // Address(0) should always revert - address returnedPool = address(0); - - vm.mockCall( - address(s_tokenAdminRegistry), - abi.encodeWithSelector(ITokenAdminRegistry.getPool.selector, destToken), - abi.encode(returnedPool) - ); - - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOffRamp.NotACompatiblePool.selector, returnedPool)); - - s_offRamp.releaseOrMintToken(amount, originalSender, OWNER, sourceTokenData, ""); - - // A contract that doesn't support the interface should also revert - returnedPool = address(s_offRamp); - - vm.mockCall( - address(s_tokenAdminRegistry), - abi.encodeWithSelector(ITokenAdminRegistry.getPool.selector, destToken), - abi.encode(returnedPool) - ); - - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOffRamp.NotACompatiblePool.selector, returnedPool)); - - s_offRamp.releaseOrMintToken(amount, originalSender, OWNER, sourceTokenData, ""); - } - - function test__releaseOrMintToken_TokenHandlingError_transfer_Revert() public { - address receiver = makeAddr("receiver"); - uint256 amount = 123123; - address token = s_sourceTokens[0]; - address destToken = s_destTokenBySourceToken[token]; - bytes memory originalSender = abi.encode(OWNER); - bytes memory offchainTokenData = abi.encode(keccak256("offchainTokenData")); - - Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), - destTokenAddress: abi.encode(destToken), - extraData: "", - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }); - - bytes memory revertData = "call reverted :o"; - - vm.mockCallRevert(destToken, abi.encodeWithSelector(IERC20.transfer.selector, receiver, amount), revertData); - - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOffRamp.TokenHandlingError.selector, revertData)); - s_offRamp.releaseOrMintToken(amount, originalSender, receiver, sourceTokenData, offchainTokenData); - } -} - -contract EVM2EVMOffRamp__releaseOrMintTokens is EVM2EVMOffRampSetup { - function test_releaseOrMintTokens_Success() public { - Client.EVMTokenAmount[] memory srcTokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); - IERC20 dstToken1 = IERC20(s_destFeeToken); - uint256 startingBalance = dstToken1.balanceOf(OWNER); - uint256 amount1 = 100; - srcTokenAmounts[0].amount = amount1; - - bytes memory originalSender = abi.encode(OWNER); - - bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); - offchainTokenData[0] = abi.encode(0x12345678); - - bytes[] memory encodedSourceTokenData = _getDefaultSourceTokenData(srcTokenAmounts); - Internal.SourceTokenData memory sourceTokenData = abi.decode(encodedSourceTokenData[0], (Internal.SourceTokenData)); - - vm.expectCall( - s_destPoolBySourceToken[srcTokenAmounts[0].token], - abi.encodeWithSelector( - LockReleaseTokenPool.releaseOrMint.selector, - Pool.ReleaseOrMintInV1({ - originalSender: originalSender, - receiver: OWNER, - amount: srcTokenAmounts[0].amount, - localToken: s_destTokenBySourceToken[srcTokenAmounts[0].token], - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: sourceTokenData.extraData, - offchainTokenData: offchainTokenData[0] - }) - ) - ); - - s_offRamp.releaseOrMintTokens( - srcTokenAmounts, originalSender, OWNER, encodedSourceTokenData, offchainTokenData, new uint32[](0) - ); - - assertEq(startingBalance + amount1, dstToken1.balanceOf(OWNER)); - } - - function test_releaseOrMintTokens_destDenominatedDecimals_Success() public { - Client.EVMTokenAmount[] memory srcTokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); - uint256 amount = 100; - uint256 destinationDenominationMultiplier = 1000; - srcTokenAmounts[1].amount = amount; - - bytes memory originalSender = abi.encode(OWNER); - bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); - bytes[] memory encodedSourceTokenData = _getDefaultSourceTokenData(srcTokenAmounts); - address pool = s_destPoolBySourceToken[srcTokenAmounts[1].token]; - address destToken = s_destTokenBySourceToken[srcTokenAmounts[1].token]; - - MaybeRevertingBurnMintTokenPool(pool).setReleaseOrMintMultiplier(destinationDenominationMultiplier); - - Client.EVMTokenAmount[] memory destTokenAmounts = s_offRamp.releaseOrMintTokens( - srcTokenAmounts, originalSender, OWNER, encodedSourceTokenData, offchainTokenData, new uint32[](0) - ); - - assertEq(destTokenAmounts[1].amount, amount * destinationDenominationMultiplier); - assertEq(destTokenAmounts[1].token, destToken); - } - - function test_OverValueWithARLOff_Success() public { - // Set a high price to trip the ARL - uint224 tokenPrice = 3 ** 128; - Internal.PriceUpdates memory priceUpdates = _getSingleTokenPriceUpdateStruct(s_destFeeToken, tokenPrice); - s_feeQuoter.updatePrices(priceUpdates); - - Client.EVMTokenAmount[] memory srcTokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); - uint256 amount1 = 100; - srcTokenAmounts[0].amount = amount1; - - bytes memory originalSender = abi.encode(OWNER); - - bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); - offchainTokenData[0] = abi.encode(0x12345678); - - bytes[] memory sourceTokenData = _getDefaultSourceTokenData(srcTokenAmounts); - - vm.expectRevert( - abi.encodeWithSelector( - RateLimiter.AggregateValueMaxCapacityExceeded.selector, - _getInboundRateLimiterConfig().capacity, - (amount1 * tokenPrice) / 1e18 - ) - ); - - // // Expect to fail from ARL - s_offRamp.releaseOrMintTokens( - srcTokenAmounts, originalSender, OWNER, sourceTokenData, offchainTokenData, new uint32[](0) - ); - - // Configure ARL off for token - EVM2EVMOffRamp.RateLimitToken[] memory removes = new EVM2EVMOffRamp.RateLimitToken[](1); - removes[0] = EVM2EVMOffRamp.RateLimitToken({sourceToken: s_sourceFeeToken, destToken: s_destFeeToken}); - s_offRamp.updateRateLimitTokens(removes, new EVM2EVMOffRamp.RateLimitToken[](0)); - - // Expect the call now succeeds - s_offRamp.releaseOrMintTokens( - srcTokenAmounts, originalSender, OWNER, sourceTokenData, offchainTokenData, new uint32[](0) - ); - } - - // Revert - - function test_TokenHandlingError_Reverts() public { - Client.EVMTokenAmount[] memory srcTokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); - - bytes memory unknownError = bytes("unknown error"); - s_maybeRevertingPool.setShouldRevert(unknownError); - - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOffRamp.TokenHandlingError.selector, unknownError)); - - s_offRamp.releaseOrMintTokens( - srcTokenAmounts, - abi.encode(OWNER), - OWNER, - _getDefaultSourceTokenData(srcTokenAmounts), - new bytes[](srcTokenAmounts.length), - new uint32[](0) - ); - } - - function test_releaseOrMintTokens_InvalidDataLengthReturnData_Revert() public { - uint256 amount = 100; - Client.EVMTokenAmount[] memory srcTokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); - srcTokenAmounts[0].amount = amount; - - bytes memory originalSender = abi.encode(OWNER); - bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); - bytes[] memory encodedSourceTokenData = _getDefaultSourceTokenData(srcTokenAmounts); - Internal.SourceTokenData memory sourceTokenData = abi.decode(encodedSourceTokenData[0], (Internal.SourceTokenData)); - - vm.mockCall( - s_destPoolBySourceToken[srcTokenAmounts[0].token], - abi.encodeWithSelector( - LockReleaseTokenPool.releaseOrMint.selector, - Pool.ReleaseOrMintInV1({ - originalSender: originalSender, - receiver: OWNER, - amount: amount, - localToken: s_destTokenBySourceToken[srcTokenAmounts[0].token], - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: sourceTokenData.extraData, - offchainTokenData: offchainTokenData[0] - }) - ), - // Includes the amount twice, this will revert due to the return data being to long - abi.encode(amount, amount) - ); - - vm.expectRevert( - abi.encodeWithSelector(EVM2EVMOffRamp.InvalidDataLength.selector, Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES, 64) - ); - - s_offRamp.releaseOrMintTokens( - srcTokenAmounts, originalSender, OWNER, encodedSourceTokenData, offchainTokenData, new uint32[](0) - ); - } - - function test_releaseOrMintTokens_InvalidEVMAddress_Revert() public { - Client.EVMTokenAmount[] memory srcTokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); - - bytes memory originalSender = abi.encode(OWNER); - bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); - bytes[] memory sourceTokenData = _getDefaultSourceTokenData(srcTokenAmounts); - bytes memory wrongAddress = abi.encode(address(1000), address(10000), address(10000)); - - sourceTokenData[0] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[srcTokenAmounts[0].token]), - destTokenAddress: wrongAddress, - extraData: "", - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }) - ); - - vm.expectRevert(abi.encodeWithSelector(Internal.InvalidEVMAddress.selector, wrongAddress)); - - s_offRamp.releaseOrMintTokens( - srcTokenAmounts, originalSender, OWNER, sourceTokenData, offchainTokenData, new uint32[](0) - ); - } - - function test_RateLimitErrors_Reverts() public { - Client.EVMTokenAmount[] memory srcTokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); - - bytes[] memory rateLimitErrors = new bytes[](5); - rateLimitErrors[0] = abi.encodeWithSelector(RateLimiter.BucketOverfilled.selector); - rateLimitErrors[1] = - abi.encodeWithSelector(RateLimiter.AggregateValueMaxCapacityExceeded.selector, uint256(100), uint256(1000)); - rateLimitErrors[2] = - abi.encodeWithSelector(RateLimiter.AggregateValueRateLimitReached.selector, uint256(42), 1, s_sourceTokens[0]); - rateLimitErrors[3] = abi.encodeWithSelector( - RateLimiter.TokenMaxCapacityExceeded.selector, uint256(100), uint256(1000), s_sourceTokens[0] - ); - rateLimitErrors[4] = - abi.encodeWithSelector(RateLimiter.TokenRateLimitReached.selector, uint256(42), 1, s_sourceTokens[0]); - - for (uint256 i = 0; i < rateLimitErrors.length; ++i) { - s_maybeRevertingPool.setShouldRevert(rateLimitErrors[i]); - - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOffRamp.TokenHandlingError.selector, rateLimitErrors[i])); - - s_offRamp.releaseOrMintTokens( - srcTokenAmounts, - abi.encode(OWNER), - OWNER, - _getDefaultSourceTokenData(srcTokenAmounts), - new bytes[](srcTokenAmounts.length), - new uint32[](0) - ); - } - } - - function test__releaseOrMintTokens_NotACompatiblePool_Reverts() public { - address fakePoolAddress = makeAddr("Doesn't exist"); - - bytes[] memory sourceTokenData = new bytes[](1); - sourceTokenData[0] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(fakePoolAddress), - destTokenAddress: abi.encode(fakePoolAddress), - extraData: "", - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }) - ); - - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOffRamp.NotACompatiblePool.selector, address(0))); - s_offRamp.releaseOrMintTokens( - new Client.EVMTokenAmount[](1), - abi.encode(makeAddr("original_sender")), - OWNER, - sourceTokenData, - new bytes[](1), - new uint32[](0) - ); - } - - function test_PriceNotFoundForToken_Reverts() public { - // Set token price to 0 - s_feeQuoter.updatePrices(_getSingleTokenPriceUpdateStruct(s_destFeeToken, 0)); - - Client.EVMTokenAmount[] memory srcTokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); - uint256 amount1 = 100; - srcTokenAmounts[0].amount = amount1; - - bytes memory originalSender = abi.encode(OWNER); - - bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); - offchainTokenData[0] = abi.encode(0x12345678); - - bytes[] memory sourceTokenData = _getDefaultSourceTokenData(srcTokenAmounts); - - vm.expectRevert(abi.encodeWithSelector(AggregateRateLimiter.PriceNotFoundForToken.selector, s_destFeeToken)); - - s_offRamp.releaseOrMintTokens( - srcTokenAmounts, originalSender, OWNER, sourceTokenData, offchainTokenData, new uint32[](0) - ); - } - - /// forge-config: default.fuzz.runs = 32 - /// forge-config: ccip.fuzz.runs = 1024 - // Uint256 gives a good range of values to test, both inside and outside of the eth address space. - function test_Fuzz__releaseOrMintTokens_AnyRevertIsCaught_Success(uint256 destPool) public { - // Input 447301751254033913445893214690834296930546521452, which is 0x4E59B44847B379578588920CA78FBF26C0B4956C - // triggers some Create2Deployer and causes it to fail - vm.assume(destPool != 447301751254033913445893214690834296930546521452); - bytes memory unusedVar = abi.encode(makeAddr("unused")); - bytes[] memory sourceTokenData = new bytes[](1); - sourceTokenData[0] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: unusedVar, - destTokenAddress: abi.encode(destPool), - extraData: unusedVar, - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }) - ); - - try s_offRamp.releaseOrMintTokens( - new Client.EVMTokenAmount[](1), unusedVar, OWNER, sourceTokenData, new bytes[](1), new uint32[](0) - ) {} catch (bytes memory reason) { - // Any revert should be a TokenHandlingError, InvalidEVMAddress, InvalidDataLength or NoContract as those are caught by the offramp - assertTrue( - bytes4(reason) == EVM2EVMOffRamp.TokenHandlingError.selector - || bytes4(reason) == Internal.InvalidEVMAddress.selector - || bytes4(reason) == EVM2EVMOffRamp.InvalidDataLength.selector - || bytes4(reason) == CallWithExactGas.NoContract.selector - || bytes4(reason) == EVM2EVMOffRamp.NotACompatiblePool.selector, - "Expected TokenHandlingError or InvalidEVMAddress" - ); - - if (destPool > type(uint160).max) { - assertEq(reason, abi.encodeWithSelector(Internal.InvalidEVMAddress.selector, abi.encode(destPool))); - } - } - } -} - -contract EVM2EVMOffRamp_getAllRateLimitTokens is EVM2EVMOffRampSetup { - function test_GetAllRateLimitTokens_Success() public view { - (address[] memory sourceTokens, address[] memory destTokens) = s_offRamp.getAllRateLimitTokens(); - - for (uint256 i = 0; i < s_sourceTokens.length; ++i) { - assertEq(s_sourceTokens[i], sourceTokens[i]); - assertEq(s_destTokens[i], destTokens[i]); - } - } -} - -contract EVM2EVMOffRamp_updateRateLimitTokens is EVM2EVMOffRampSetup { - function setUp() public virtual override { - super.setUp(); - // Clear rate limit tokens state - EVM2EVMOffRamp.RateLimitToken[] memory remove = new EVM2EVMOffRamp.RateLimitToken[](s_sourceTokens.length); - for (uint256 i = 0; i < s_sourceTokens.length; ++i) { - remove[i] = EVM2EVMOffRamp.RateLimitToken({sourceToken: s_sourceTokens[i], destToken: s_destTokens[i]}); - } - s_offRamp.updateRateLimitTokens(remove, new EVM2EVMOffRamp.RateLimitToken[](0)); - } - - function test_updateRateLimitTokens_Success() public { - EVM2EVMOffRamp.RateLimitToken[] memory adds = new EVM2EVMOffRamp.RateLimitToken[](2); - adds[0] = EVM2EVMOffRamp.RateLimitToken({sourceToken: s_sourceTokens[0], destToken: s_destTokens[0]}); - adds[1] = EVM2EVMOffRamp.RateLimitToken({sourceToken: s_sourceTokens[1], destToken: s_destTokens[1]}); - - for (uint256 i = 0; i < adds.length; ++i) { - vm.expectEmit(); - emit EVM2EVMOffRamp.TokenAggregateRateLimitAdded(adds[i].sourceToken, adds[i].destToken); - } - - s_offRamp.updateRateLimitTokens(new EVM2EVMOffRamp.RateLimitToken[](0), adds); - - (address[] memory sourceTokens, address[] memory destTokens) = s_offRamp.getAllRateLimitTokens(); - - for (uint256 i = 0; i < adds.length; ++i) { - assertEq(adds[i].sourceToken, sourceTokens[i]); - assertEq(adds[i].destToken, destTokens[i]); - } - } - - function test_updateRateLimitTokens_AddsAndRemoves_Success() public { - EVM2EVMOffRamp.RateLimitToken[] memory adds = new EVM2EVMOffRamp.RateLimitToken[](3); - adds[0] = EVM2EVMOffRamp.RateLimitToken({sourceToken: s_sourceTokens[0], destToken: s_destTokens[0]}); - adds[1] = EVM2EVMOffRamp.RateLimitToken({sourceToken: s_sourceTokens[1], destToken: s_destTokens[1]}); - // Add a duplicate, this should not revert the tx - adds[2] = EVM2EVMOffRamp.RateLimitToken({sourceToken: s_sourceTokens[1], destToken: s_destTokens[1]}); - - EVM2EVMOffRamp.RateLimitToken[] memory removes = new EVM2EVMOffRamp.RateLimitToken[](1); - removes[0] = adds[0]; - - for (uint256 i = 0; i < adds.length - 1; ++i) { - vm.expectEmit(); - emit EVM2EVMOffRamp.TokenAggregateRateLimitAdded(adds[i].sourceToken, adds[i].destToken); - } - - s_offRamp.updateRateLimitTokens(removes, adds); - - for (uint256 i = 0; i < removes.length; ++i) { - vm.expectEmit(); - emit EVM2EVMOffRamp.TokenAggregateRateLimitRemoved(removes[i].sourceToken, removes[i].destToken); - } - - s_offRamp.updateRateLimitTokens(removes, new EVM2EVMOffRamp.RateLimitToken[](0)); - - (address[] memory sourceTokens, address[] memory destTokens) = s_offRamp.getAllRateLimitTokens(); - - assertEq(1, sourceTokens.length); - assertEq(adds[1].sourceToken, sourceTokens[0]); - - assertEq(1, destTokens.length); - assertEq(adds[1].destToken, destTokens[0]); - } - - function test_Fuzz_UpdateRateLimitTokens(uint8 numTokens) public { - // Needs to be more than 1 so that the division doesn't round down and the even makes the comparisons simpler - vm.assume(numTokens > 1 && numTokens % 2 == 0); - - // Clear the Rate limit tokens array so the test can start from a baseline - (address[] memory sourceTokens, address[] memory destTokens) = s_offRamp.getAllRateLimitTokens(); - EVM2EVMOffRamp.RateLimitToken[] memory removes = new EVM2EVMOffRamp.RateLimitToken[](sourceTokens.length); - for (uint256 x = 0; x < removes.length; x++) { - removes[x] = EVM2EVMOffRamp.RateLimitToken({sourceToken: sourceTokens[x], destToken: destTokens[x]}); - } - s_offRamp.updateRateLimitTokens(removes, new EVM2EVMOffRamp.RateLimitToken[](0)); - - // Sanity check that the rateLimitTokens were successfully cleared - (sourceTokens, destTokens) = s_offRamp.getAllRateLimitTokens(); - assertEq(sourceTokens.length, 0, "sourceTokenLength should be zero"); - - EVM2EVMOffRamp.RateLimitToken[] memory adds = new EVM2EVMOffRamp.RateLimitToken[](numTokens); - - for (uint256 x = 0; x < numTokens; x++) { - address tokenAddr = vm.addr(x + 1); - - // Create an array of several fake tokens to add which are deployed on the same address on both chains for simplicity - adds[x] = EVM2EVMOffRamp.RateLimitToken({sourceToken: tokenAddr, destToken: tokenAddr}); - } - - // Attempt to add the tokens to the RateLimitToken Array - s_offRamp.updateRateLimitTokens(new EVM2EVMOffRamp.RateLimitToken[](0), adds); - - // Retrieve them from storage and make sure that they all match the expected adds - (sourceTokens, destTokens) = s_offRamp.getAllRateLimitTokens(); - - for (uint256 x = 0; x < sourceTokens.length; x++) { - // Check that the tokens match the ones we generated earlier - assertEq(sourceTokens[x], adds[x].sourceToken, "Source token doesn't match add"); - assertEq(destTokens[x], adds[x].sourceToken, "dest Token doesn't match add"); - } - - // Attempt to remove half of the numTokens by removing the second half of the list and copying it to a removes array - removes = new EVM2EVMOffRamp.RateLimitToken[](adds.length / 2); - - for (uint256 x = 0; x < adds.length / 2; x++) { - removes[x] = adds[x + (adds.length / 2)]; - } - - // Attempt to update again, this time adding nothing and removing the second half of the tokens - s_offRamp.updateRateLimitTokens(removes, new EVM2EVMOffRamp.RateLimitToken[](0)); - - (sourceTokens, destTokens) = s_offRamp.getAllRateLimitTokens(); - assertEq(sourceTokens.length, adds.length / 2, "Current Rate limit token length is not half of the original adds"); - for (uint256 x = 0; x < sourceTokens.length; x++) { - // Check that the tokens match the ones we generated earlier and didn't remove in the previous step - assertEq(sourceTokens[x], adds[x].sourceToken, "Source token doesn't match add after removes"); - assertEq(destTokens[x], adds[x].destToken, "dest Token doesn't match add after removes"); - } - } - - // Reverts - - function test_updateRateLimitTokens_NonOwner_Revert() public { - EVM2EVMOffRamp.RateLimitToken[] memory addsAndRemoves = new EVM2EVMOffRamp.RateLimitToken[](4); - - vm.startPrank(STRANGER); - - vm.expectRevert("Only callable by owner"); - - s_offRamp.updateRateLimitTokens(addsAndRemoves, addsAndRemoves); - } -} diff --git a/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMOffRampSetup.t.sol b/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMOffRampSetup.t.sol deleted file mode 100644 index 8399637718c..00000000000 --- a/contracts/src/v0.8/ccip/test/offRamp/EVM2EVMOffRampSetup.t.sol +++ /dev/null @@ -1,262 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {IAny2EVMMessageReceiver} from "../../interfaces/IAny2EVMMessageReceiver.sol"; -import {ICommitStore} from "../../interfaces/ICommitStore.sol"; - -import {Router} from "../../Router.sol"; -import {Client} from "../../libraries/Client.sol"; -import {Internal} from "../../libraries/Internal.sol"; -import {EVM2EVMOffRamp} from "../../offRamp/EVM2EVMOffRamp.sol"; -import {TokenPool} from "../../pools/TokenPool.sol"; -import {TokenSetup} from "../TokenSetup.t.sol"; - -import {FeeQuoterSetup} from "../feeQuoter/FeeQuoterSetup.t.sol"; -import {EVM2EVMOffRampHelper} from "../helpers/EVM2EVMOffRampHelper.sol"; -import {MaybeRevertingBurnMintTokenPool} from "../helpers/MaybeRevertingBurnMintTokenPool.sol"; -import {MaybeRevertMessageReceiver} from "../helpers/receivers/MaybeRevertMessageReceiver.sol"; -import {MockCommitStore} from "../mocks/MockCommitStore.sol"; -import {OCR2BaseSetup} from "../ocr/OCR2Base.t.sol"; - -contract EVM2EVMOffRampSetup is TokenSetup, FeeQuoterSetup, OCR2BaseSetup { - MockCommitStore internal s_mockCommitStore; - IAny2EVMMessageReceiver internal s_receiver; - IAny2EVMMessageReceiver internal s_secondary_receiver; - MaybeRevertMessageReceiver internal s_reverting_receiver; - - MaybeRevertingBurnMintTokenPool internal s_maybeRevertingPool; - - EVM2EVMOffRampHelper internal s_offRamp; - address internal s_sourceTokenPool = makeAddr("sourceTokenPool"); - - function setUp() public virtual override(TokenSetup, FeeQuoterSetup, OCR2BaseSetup) { - TokenSetup.setUp(); - FeeQuoterSetup.setUp(); - OCR2BaseSetup.setUp(); - - s_mockCommitStore = new MockCommitStore(); - s_receiver = new MaybeRevertMessageReceiver(false); - s_secondary_receiver = new MaybeRevertMessageReceiver(false); - s_reverting_receiver = new MaybeRevertMessageReceiver(true); - - s_maybeRevertingPool = MaybeRevertingBurnMintTokenPool(s_destPoolByToken[s_destTokens[1]]); - - deployOffRamp(s_mockCommitStore, s_destRouter, address(0)); - } - - function deployOffRamp(ICommitStore commitStore, Router router, address prevOffRamp) internal { - s_offRamp = new EVM2EVMOffRampHelper( - EVM2EVMOffRamp.StaticConfig({ - commitStore: address(commitStore), - chainSelector: DEST_CHAIN_SELECTOR, - sourceChainSelector: SOURCE_CHAIN_SELECTOR, - onRamp: ON_RAMP_ADDRESS, - prevOffRamp: prevOffRamp, - rmnProxy: address(s_mockRMN), - tokenAdminRegistry: address(s_tokenAdminRegistry) - }), - _getInboundRateLimiterConfig() - ); - s_offRamp.setOCR2Config( - s_valid_signers, - s_valid_transmitters, - s_f, - abi.encode(generateDynamicOffRampConfig(address(router), address(s_feeQuoter))), - s_offchainConfigVersion, - abi.encode("") - ); - - Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](0); - Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](2); - offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: address(s_offRamp)}); - offRampUpdates[1] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: address(prevOffRamp)}); - s_destRouter.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); - EVM2EVMOffRamp.RateLimitToken[] memory tokensToAdd = new EVM2EVMOffRamp.RateLimitToken[](s_sourceTokens.length); - for (uint256 i = 0; i < s_sourceTokens.length; ++i) { - tokensToAdd[i] = EVM2EVMOffRamp.RateLimitToken({sourceToken: s_sourceTokens[i], destToken: s_destTokens[i]}); - } - s_offRamp.updateRateLimitTokens(new EVM2EVMOffRamp.RateLimitToken[](0), tokensToAdd); - } - - function generateDynamicOffRampConfig( - address router, - address priceRegistry - ) internal pure returns (EVM2EVMOffRamp.DynamicConfig memory) { - return EVM2EVMOffRamp.DynamicConfig({ - permissionLessExecutionThresholdSeconds: PERMISSION_LESS_EXECUTION_THRESHOLD_SECONDS, - router: router, - priceRegistry: priceRegistry, - maxNumberOfTokensPerMsg: MAX_TOKENS_LENGTH, - maxDataBytes: MAX_DATA_SIZE - }); - } - - function _convertToGeneralMessage( - Internal.EVM2EVMMessage memory original - ) internal view returns (Client.Any2EVMMessage memory message) { - uint256 numberOfTokens = original.tokenAmounts.length; - Client.EVMTokenAmount[] memory destTokenAmounts = new Client.EVMTokenAmount[](numberOfTokens); - - for (uint256 i = 0; i < numberOfTokens; ++i) { - Internal.SourceTokenData memory sourceTokenData = - abi.decode(original.sourceTokenData[i], (Internal.SourceTokenData)); - - address destPoolAddress = abi.decode(sourceTokenData.destTokenAddress, (address)); - TokenPool pool = TokenPool(destPoolAddress); - destTokenAmounts[i].token = address(pool.getToken()); - destTokenAmounts[i].amount = original.tokenAmounts[i].amount; - } - - return Client.Any2EVMMessage({ - messageId: original.messageId, - sourceChainSelector: original.sourceChainSelector, - sender: abi.encode(original.sender), - data: original.data, - destTokenAmounts: destTokenAmounts - }); - } - - function _generateAny2EVMMessageNoTokens( - uint64 sequenceNumber - ) internal view returns (Internal.EVM2EVMMessage memory) { - return _generateAny2EVMMessage(sequenceNumber, new Client.EVMTokenAmount[](0), false); - } - - function _generateAny2EVMMessageWithTokens( - uint64 sequenceNumber, - uint256[] memory amounts - ) internal view returns (Internal.EVM2EVMMessage memory) { - Client.EVMTokenAmount[] memory tokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); - for (uint256 i = 0; i < tokenAmounts.length; ++i) { - tokenAmounts[i].amount = amounts[i]; - } - return _generateAny2EVMMessage(sequenceNumber, tokenAmounts, false); - } - - function _generateAny2EVMMessage( - uint64 sequenceNumber, - Client.EVMTokenAmount[] memory tokenAmounts, - bool allowOutOfOrderExecution - ) internal view returns (Internal.EVM2EVMMessage memory) { - bytes memory data = abi.encode(0); - Internal.EVM2EVMMessage memory message = Internal.EVM2EVMMessage({ - sequenceNumber: sequenceNumber, - sender: OWNER, - nonce: allowOutOfOrderExecution ? 0 : sequenceNumber, - gasLimit: GAS_LIMIT, - strict: false, - sourceChainSelector: SOURCE_CHAIN_SELECTOR, - receiver: address(s_receiver), - data: data, - tokenAmounts: tokenAmounts, - sourceTokenData: new bytes[](tokenAmounts.length), - feeToken: s_destFeeToken, - feeTokenAmount: uint256(0), - messageId: "" - }); - - // Correctly set the TokenDataPayload for each token. Tokens have to be set up in the TokenSetup. - for (uint256 i = 0; i < tokenAmounts.length; ++i) { - message.sourceTokenData[i] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[tokenAmounts[i].token]), - destTokenAddress: abi.encode(s_destTokenBySourceToken[tokenAmounts[i].token]), - extraData: "", - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }) - ); - } - - message.messageId = Internal._hash( - message, - keccak256( - abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, SOURCE_CHAIN_SELECTOR, DEST_CHAIN_SELECTOR, ON_RAMP_ADDRESS) - ) - ); - - return message; - } - - function _generateSingleBasicMessage() internal view returns (Internal.EVM2EVMMessage[] memory) { - Internal.EVM2EVMMessage[] memory messages = new Internal.EVM2EVMMessage[](1); - messages[0] = _generateAny2EVMMessageNoTokens(1); - return messages; - } - - function _generateSingleBasicMessageWithTokens() internal view returns (Internal.EVM2EVMMessage[] memory) { - Internal.EVM2EVMMessage[] memory messages = new Internal.EVM2EVMMessage[](1); - Client.EVMTokenAmount[] memory tokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); - tokenAmounts[0].amount = 1e18; - messages[0] = _generateAny2EVMMessage(1, tokenAmounts, false); - return messages; - } - - function _generateMessagesWithTokens() internal view returns (Internal.EVM2EVMMessage[] memory) { - Internal.EVM2EVMMessage[] memory messages = new Internal.EVM2EVMMessage[](2); - Client.EVMTokenAmount[] memory tokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); - tokenAmounts[0].amount = 1e18; - tokenAmounts[1].amount = 5e18; - messages[0] = _generateAny2EVMMessage(1, tokenAmounts, false); - messages[1] = _generateAny2EVMMessage(2, tokenAmounts, false); - - return messages; - } - - function _generateReportFromMessages( - Internal.EVM2EVMMessage[] memory messages - ) internal pure returns (Internal.ExecutionReport memory) { - bytes[][] memory offchainTokenData = new bytes[][](messages.length); - - for (uint256 i = 0; i < messages.length; ++i) { - offchainTokenData[i] = new bytes[](messages[i].tokenAmounts.length); - } - - return Internal.ExecutionReport({ - proofs: new bytes32[](0), - proofFlagBits: 2 ** 256 - 1, - messages: messages, - offchainTokenData: offchainTokenData - }); - } - - function _getGasLimitsFromMessages( - Internal.EVM2EVMMessage[] memory messages - ) internal pure returns (EVM2EVMOffRamp.GasLimitOverride[] memory) { - EVM2EVMOffRamp.GasLimitOverride[] memory gasLimitOverrides = new EVM2EVMOffRamp.GasLimitOverride[](messages.length); - for (uint256 i = 0; i < messages.length; ++i) { - gasLimitOverrides[i].receiverExecutionGasLimit = messages[i].gasLimit; - gasLimitOverrides[i].tokenGasOverrides = new uint32[](messages[i].tokenAmounts.length); - - for (uint256 j = 0; j < messages[i].tokenAmounts.length; ++j) { - gasLimitOverrides[i].tokenGasOverrides[j] = DEFAULT_TOKEN_DEST_GAS_OVERHEAD + 1; - } - } - - return gasLimitOverrides; - } - - function _assertSameConfig(EVM2EVMOffRamp.DynamicConfig memory a, EVM2EVMOffRamp.DynamicConfig memory b) public pure { - assertEq(a.permissionLessExecutionThresholdSeconds, b.permissionLessExecutionThresholdSeconds); - assertEq(a.router, b.router); - assertEq(a.priceRegistry, b.priceRegistry); - assertEq(a.maxNumberOfTokensPerMsg, b.maxNumberOfTokensPerMsg); - assertEq(a.maxDataBytes, b.maxDataBytes); - } - - function _getDefaultSourceTokenData( - Client.EVMTokenAmount[] memory srcTokenAmounts - ) internal view returns (bytes[] memory) { - bytes[] memory sourceTokenData = new bytes[](srcTokenAmounts.length); - for (uint256 i = 0; i < srcTokenAmounts.length; ++i) { - sourceTokenData[i] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[srcTokenAmounts[i].token]), - destTokenAddress: abi.encode(s_destTokenBySourceToken[srcTokenAmounts[i].token]), - extraData: "", - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }) - ); - } - return sourceTokenData; - } -} diff --git a/contracts/src/v0.8/ccip/test/offRamp/OffRamp.t.sol b/contracts/src/v0.8/ccip/test/offRamp/OffRamp.t.sol index 9cf0321a853..b11a60a74b7 100644 --- a/contracts/src/v0.8/ccip/test/offRamp/OffRamp.t.sol +++ b/contracts/src/v0.8/ccip/test/offRamp/OffRamp.t.sol @@ -607,7 +607,7 @@ contract OffRamp_executeSingleReport is OffRampSetup { Internal.Any2EVMRampMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - Internal.ExecutionReportSingleChain memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + Internal.ExecutionReport memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); vm.resumeGasMetering(); vm.recordLogs(); @@ -630,7 +630,7 @@ contract OffRamp_executeSingleReport is OffRampSetup { messages[1].receiver = address(s_secondary_receiver); messages[1].header.messageId = _hashMessage(messages[1], ON_RAMP_ADDRESS_1); - Internal.ExecutionReportSingleChain memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + Internal.ExecutionReport memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); vm.resumeGasMetering(); vm.recordLogs(); @@ -813,8 +813,7 @@ contract OffRamp_executeSingleReport is OffRampSetup { _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3); messages[0].header.destChainSelector = DEST_CHAIN_SELECTOR + 1; - Internal.ExecutionReportSingleChain memory executionReport = - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + Internal.ExecutionReport memory executionReport = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); vm.expectRevert( abi.encodeWithSelector(OffRamp.InvalidMessageDestChainSelector.selector, messages[0].header.destChainSelector) @@ -845,7 +844,7 @@ contract OffRamp_executeSingleReport is OffRampSetup { } function test_UnexpectedTokenData_Revert() public { - Internal.ExecutionReportSingleChain memory report = _generateReportFromMessages( + Internal.ExecutionReport memory report = _generateReportFromMessages( SOURCE_CHAIN_SELECTOR_1, _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1) ); report.offchainTokenData = new bytes[][](report.messages.length + 1); @@ -858,7 +857,7 @@ contract OffRamp_executeSingleReport is OffRampSetup { function test_EmptyReport_Revert() public { vm.expectRevert(OffRamp.EmptyReport.selector); s_offRamp.executeSingleReport( - Internal.ExecutionReportSingleChain({ + Internal.ExecutionReport({ sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, proofs: new bytes32[](0), proofFlagBits: 0, @@ -917,7 +916,7 @@ contract OffRamp_executeSingleReport is OffRampSetup { function test_TokenDataMismatch_Revert() public { Internal.Any2EVMRampMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - Internal.ExecutionReportSingleChain memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + Internal.ExecutionReport memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); report.offchainTokenData[0] = new bytes[](messages[0].tokenAmounts.length + 1); @@ -938,8 +937,7 @@ contract OffRamp_executeSingleReport is OffRampSetup { messages[0].receiver = address(new ConformingReceiver(address(s_destRouter), s_destFeeToken)); messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); - Internal.ExecutionReportSingleChain memory executionReport = - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + Internal.ExecutionReport memory executionReport = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); vm.recordLogs(); s_offRamp.executeSingleReport(executionReport, new OffRamp.GasLimitOverride[](0)); @@ -1194,7 +1192,7 @@ contract OffRamp_batchExecute is OffRampSetup { messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 3); - Internal.ExecutionReportSingleChain[] memory reports = new Internal.ExecutionReportSingleChain[](2); + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages2); @@ -1244,7 +1242,7 @@ contract OffRamp_batchExecute is OffRampSetup { messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3, 1); - Internal.ExecutionReportSingleChain[] memory reports = new Internal.ExecutionReportSingleChain[](2); + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messages2); @@ -1302,7 +1300,7 @@ contract OffRamp_batchExecute is OffRampSetup { messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3, 1); - Internal.ExecutionReportSingleChain[] memory reports = new Internal.ExecutionReportSingleChain[](2); + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messages2); @@ -1335,7 +1333,7 @@ contract OffRamp_batchExecute is OffRampSetup { Internal.Any2EVMRampMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - Internal.ExecutionReportSingleChain[] memory reports = new Internal.ExecutionReportSingleChain[](2); + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); @@ -1381,7 +1379,7 @@ contract OffRamp_batchExecute is OffRampSetup { // Reverts function test_ZeroReports_Revert() public { vm.expectRevert(OffRamp.EmptyReport.selector); - s_offRamp.batchExecute(new Internal.ExecutionReportSingleChain[](0), new OffRamp.GasLimitOverride[][](1)); + s_offRamp.batchExecute(new Internal.ExecutionReport[](0), new OffRamp.GasLimitOverride[][](1)); } function test_OutOfBoundsGasLimitsAccess_Revert() public { @@ -1392,7 +1390,7 @@ contract OffRamp_batchExecute is OffRampSetup { messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 3); - Internal.ExecutionReportSingleChain[] memory reports = new Internal.ExecutionReportSingleChain[](2); + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages2); @@ -1511,7 +1509,7 @@ contract OffRamp_manuallyExecute is OffRampSetup { messages2[i].header.messageId = _hashMessage(messages2[i], ON_RAMP_ADDRESS_3); } - Internal.ExecutionReportSingleChain[] memory reports = new Internal.ExecutionReportSingleChain[](2); + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messages2); @@ -1678,8 +1676,7 @@ contract OffRamp_manuallyExecute is OffRampSetup { Internal.Any2EVMRampMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - Internal.ExecutionReportSingleChain[] memory reports = - _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + Internal.ExecutionReport[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); uint256 chain1 = block.chainid; uint256 chain2 = chain1 + 1; vm.chainId(chain2); @@ -1696,8 +1693,7 @@ contract OffRamp_manuallyExecute is OffRampSetup { messages[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); messages[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); - Internal.ExecutionReportSingleChain[] memory reports = - _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + Internal.ExecutionReport[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); // No overrides for report vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); @@ -1730,7 +1726,7 @@ contract OffRamp_manuallyExecute is OffRampSetup { messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3, 1); - Internal.ExecutionReportSingleChain[] memory reports = new Internal.ExecutionReportSingleChain[](2); + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messages2); @@ -1889,7 +1885,7 @@ contract OffRamp_manuallyExecute is OffRampSetup { messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); - Internal.ExecutionReportSingleChain memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + Internal.ExecutionReport memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); // sets the report to be repeated on the ReentrancyAbuser to be able to replay receiver.setPayload(report); @@ -1936,7 +1932,7 @@ contract OffRamp_manuallyExecute is OffRampSetup { messages2[i].header.messageId = _hashMessage(messages2[i], ON_RAMP_ADDRESS_3); } - Internal.ExecutionReportSingleChain[] memory reports = new Internal.ExecutionReportSingleChain[](2); + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messages2); @@ -1957,7 +1953,7 @@ contract OffRamp_manuallyExecute is OffRampSetup { messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); - Internal.ExecutionReportSingleChain[] memory reports = new Internal.ExecutionReportSingleChain[](2); + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messages2); @@ -1985,8 +1981,7 @@ contract OffRamp_execute is OffRampSetup { function test_SingleReport_Success() public { Internal.Any2EVMRampMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - Internal.ExecutionReportSingleChain[] memory reports = - _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + Internal.ExecutionReport[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); vm.expectEmit(); emit MultiOCR3Base.Transmitted( @@ -2015,7 +2010,7 @@ contract OffRamp_execute is OffRampSetup { messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 3); - Internal.ExecutionReportSingleChain[] memory reports = new Internal.ExecutionReportSingleChain[](2); + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages2); @@ -2061,7 +2056,7 @@ contract OffRamp_execute is OffRampSetup { } function test_LargeBatch_Success() public { - Internal.ExecutionReportSingleChain[] memory reports = new Internal.ExecutionReportSingleChain[](10); + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](10); for (uint64 i = 0; i < reports.length; ++i) { Internal.Any2EVMRampMessage[] memory messages = new Internal.Any2EVMRampMessage[](3); messages[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1 + i * 3); @@ -2106,7 +2101,7 @@ contract OffRamp_execute is OffRampSetup { messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 3); - Internal.ExecutionReportSingleChain[] memory reports = new Internal.ExecutionReportSingleChain[](2); + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages2); @@ -2167,8 +2162,7 @@ contract OffRamp_execute is OffRampSetup { Internal.Any2EVMRampMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - Internal.ExecutionReportSingleChain[] memory reports = - _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + Internal.ExecutionReport[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); vm.expectRevert(MultiOCR3Base.UnauthorizedTransmitter.selector); s_offRamp.execute(reportContext, abi.encode(reports)); @@ -2180,8 +2174,7 @@ contract OffRamp_execute is OffRampSetup { Internal.Any2EVMRampMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - Internal.ExecutionReportSingleChain[] memory reports = - _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + Internal.ExecutionReport[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); bytes32[3] memory reportContext = [bytes32(""), s_configDigestExec, s_configDigestExec]; @@ -2207,8 +2200,7 @@ contract OffRamp_execute is OffRampSetup { Internal.Any2EVMRampMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - Internal.ExecutionReportSingleChain[] memory reports = - _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + Internal.ExecutionReport[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); bytes32[3] memory reportContext = [bytes32(""), s_configDigestExec, s_configDigestExec]; @@ -2236,15 +2228,14 @@ contract OffRamp_execute is OffRampSetup { Internal.Any2EVMRampMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - Internal.ExecutionReportSingleChain[] memory reports = - _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + Internal.ExecutionReport[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); vm.expectRevert(); _execute(reports); } function test_ZeroReports_Revert() public { - Internal.ExecutionReportSingleChain[] memory reports = new Internal.ExecutionReportSingleChain[](0); + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](0); vm.expectRevert(OffRamp.EmptyReport.selector); _execute(reports); @@ -2266,7 +2257,7 @@ contract OffRamp_execute is OffRampSetup { Internal.Any2EVMRampMessage[] memory messages = _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - Internal.ExecutionReportSingleChain memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + Internal.ExecutionReport memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); vm.startPrank(s_validTransmitters[0]); vm.expectRevert(); diff --git a/contracts/src/v0.8/ccip/test/offRamp/OffRampSetup.t.sol b/contracts/src/v0.8/ccip/test/offRamp/OffRampSetup.t.sol index 0fb4ff50942..580a712553d 100644 --- a/contracts/src/v0.8/ccip/test/offRamp/OffRampSetup.t.sol +++ b/contracts/src/v0.8/ccip/test/offRamp/OffRampSetup.t.sol @@ -7,16 +7,13 @@ import {IRMNRemote} from "../../interfaces/IRMNRemote.sol"; import {AuthorizedCallers} from "../../../shared/access/AuthorizedCallers.sol"; import {NonceManager} from "../../NonceManager.sol"; -import {RMN} from "../../RMN.sol"; import {Router} from "../../Router.sol"; import {Client} from "../../libraries/Client.sol"; import {Internal} from "../../libraries/Internal.sol"; import {MultiOCR3Base} from "../../ocr/MultiOCR3Base.sol"; -import {EVM2EVMOffRamp} from "../../offRamp/EVM2EVMOffRamp.sol"; import {OffRamp} from "../../offRamp/OffRamp.sol"; import {TokenPool} from "../../pools/TokenPool.sol"; import {FeeQuoterSetup} from "../feeQuoter/FeeQuoterSetup.t.sol"; -import {EVM2EVMOffRampHelper} from "../helpers/EVM2EVMOffRampHelper.sol"; import {MaybeRevertingBurnMintTokenPool} from "../helpers/MaybeRevertingBurnMintTokenPool.sol"; import {MessageInterceptorHelper} from "../helpers/MessageInterceptorHelper.sol"; import {OffRampHelper} from "../helpers/OffRampHelper.sol"; @@ -44,7 +41,6 @@ contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { OffRampHelper internal s_offRamp; MessageInterceptorHelper internal s_inboundMessageInterceptor; NonceManager internal s_inboundNonceManager; - RMN internal s_realRMN; address internal s_sourceTokenPool = makeAddr("sourceTokenPool"); bytes32 internal s_configDigestExec; @@ -120,49 +116,10 @@ contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { s_feeQuoter.applyAuthorizedCallerUpdates( AuthorizedCallers.AuthorizedCallerArgs({addedCallers: priceUpdaters, removedCallers: new address[](0)}) ); - } - - // TODO: function can be made common across OffRampSetup and MultiOffRampSetup - function _deploySingleLaneOffRamp( - ICommitStore commitStore, - Router router, - address prevOffRamp, - uint64 sourceChainSelector, - address onRampAddress - ) internal returns (EVM2EVMOffRampHelper) { - EVM2EVMOffRampHelper offRamp = new EVM2EVMOffRampHelper( - EVM2EVMOffRamp.StaticConfig({ - commitStore: address(commitStore), - chainSelector: DEST_CHAIN_SELECTOR, - sourceChainSelector: sourceChainSelector, - onRamp: onRampAddress, - prevOffRamp: prevOffRamp, - rmnProxy: address(s_mockRMN), - tokenAdminRegistry: address(s_tokenAdminRegistry) - }), - _getInboundRateLimiterConfig() - ); - offRamp.setOCR2Config( - s_validSigners, - s_validTransmitters, - s_F, - abi.encode(_generateDynamicOffRampConfig(address(router), address(s_feeQuoter))), - s_offchainConfigVersion, - abi.encode("") - ); - - Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](0); - Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](2); - offRampUpdates[0] = Router.OffRamp({sourceChainSelector: sourceChainSelector, offRamp: address(s_offRamp)}); - offRampUpdates[1] = Router.OffRamp({sourceChainSelector: sourceChainSelector, offRamp: address(prevOffRamp)}); - s_destRouter.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); - EVM2EVMOffRamp.RateLimitToken[] memory tokensToAdd = new EVM2EVMOffRamp.RateLimitToken[](s_sourceTokens.length); - for (uint256 i = 0; i < s_sourceTokens.length; ++i) { - tokensToAdd[i] = EVM2EVMOffRamp.RateLimitToken({sourceToken: s_sourceTokens[i], destToken: s_destTokens[i]}); - } - offRamp.updateRateLimitTokens(new EVM2EVMOffRamp.RateLimitToken[](0), tokensToAdd); - return offRamp; + Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); + offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: address(s_offRamp)}); + s_destRouter.applyRampUpdates(new Router.OnRamp[](0), new Router.OffRamp[](0), offRampUpdates); } function _setupMultipleOffRamps() internal { @@ -207,19 +164,6 @@ contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { s_destRouter.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); } - function _generateDynamicOffRampConfig( - address router, - address priceRegistry - ) internal pure returns (EVM2EVMOffRamp.DynamicConfig memory) { - return EVM2EVMOffRamp.DynamicConfig({ - permissionLessExecutionThresholdSeconds: PERMISSION_LESS_EXECUTION_THRESHOLD_SECONDS, - router: router, - priceRegistry: priceRegistry, - maxNumberOfTokensPerMsg: MAX_TOKENS_LENGTH, - maxDataBytes: MAX_DATA_SIZE - }); - } - uint32 internal constant MAX_TOKEN_POOL_RELEASE_OR_MINT_GAS = 200_000; uint32 internal constant MAX_TOKEN_POOL_TRANSFER_GAS = 50_000; @@ -345,14 +289,14 @@ contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { function _generateReportFromMessages( uint64 sourceChainSelector, Internal.Any2EVMRampMessage[] memory messages - ) internal pure returns (Internal.ExecutionReportSingleChain memory) { + ) internal pure returns (Internal.ExecutionReport memory) { bytes[][] memory offchainTokenData = new bytes[][](messages.length); for (uint256 i = 0; i < messages.length; ++i) { offchainTokenData[i] = new bytes[](messages[i].tokenAmounts.length); } - return Internal.ExecutionReportSingleChain({ + return Internal.ExecutionReport({ sourceChainSelector: sourceChainSelector, proofs: new bytes32[](0), proofFlagBits: 2 ** 256 - 1, @@ -364,8 +308,8 @@ contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { function _generateBatchReportFromMessages( uint64 sourceChainSelector, Internal.Any2EVMRampMessage[] memory messages - ) internal pure returns (Internal.ExecutionReportSingleChain[] memory) { - Internal.ExecutionReportSingleChain[] memory reports = new Internal.ExecutionReportSingleChain[](1); + ) internal pure returns (Internal.ExecutionReport[] memory) { + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](1); reports[0] = _generateReportFromMessages(sourceChainSelector, messages); return reports; } @@ -445,14 +389,6 @@ contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { ); } - function _setupRealRMN() internal { - RMN.Voter[] memory voters = new RMN.Voter[](1); - voters[0] = - RMN.Voter({blessVoteAddr: BLESS_VOTE_ADDR, curseVoteAddr: address(9999), blessWeight: 1, curseWeight: 1}); - // Overwrite base mock rmnRemote with real. - s_realRMN = new RMN(RMN.Config({voters: voters, blessWeightThreshold: 1, curseWeightThreshold: 1})); - } - function _commit(OffRamp.CommitReport memory commitReport, uint64 sequenceNumber) internal { bytes32[3] memory reportContext = [s_configDigestCommit, bytes32(uint256(sequenceNumber)), s_configDigestCommit]; @@ -463,7 +399,7 @@ contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { s_offRamp.commit(reportContext, abi.encode(commitReport), rs, ss, rawVs); } - function _execute(Internal.ExecutionReportSingleChain[] memory reports) internal { + function _execute(Internal.ExecutionReport[] memory reports) internal { bytes32[3] memory reportContext = [s_configDigestExec, s_configDigestExec, s_configDigestExec]; vm.startPrank(s_validTransmitters[0]); diff --git a/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMOnRamp.t.sol b/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMOnRamp.t.sol deleted file mode 100644 index f0cd0f80edb..00000000000 --- a/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMOnRamp.t.sol +++ /dev/null @@ -1,2051 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {ITokenAdminRegistry} from "../../interfaces/ITokenAdminRegistry.sol"; - -import {BurnMintERC677} from "../../../shared/token/ERC677/BurnMintERC677.sol"; -import {AggregateRateLimiter} from "../../AggregateRateLimiter.sol"; -import {Pool} from "../../libraries/Pool.sol"; -import {RateLimiter} from "../../libraries/RateLimiter.sol"; -import {USDPriceWith18Decimals} from "../../libraries/USDPriceWith18Decimals.sol"; -import {EVM2EVMOnRamp} from "../../onRamp/EVM2EVMOnRamp.sol"; -import {LockReleaseTokenPool} from "../../pools/LockReleaseTokenPool.sol"; -import {TokenPool} from "../../pools/TokenPool.sol"; -import {MaybeRevertingBurnMintTokenPool} from "../helpers/MaybeRevertingBurnMintTokenPool.sol"; -import "./EVM2EVMOnRampSetup.t.sol"; - -contract EVM2EVMOnRamp_constructor is EVM2EVMOnRampSetup { - function test_Constructor_Success() public { - EVM2EVMOnRamp.StaticConfig memory staticConfig = EVM2EVMOnRamp.StaticConfig({ - linkToken: s_sourceTokens[0], - chainSelector: SOURCE_CHAIN_SELECTOR, - destChainSelector: DEST_CHAIN_SELECTOR, - defaultTxGasLimit: GAS_LIMIT, - maxNopFeesJuels: MAX_NOP_FEES_JUELS, - prevOnRamp: address(0), - rmnProxy: address(s_mockRMN), - tokenAdminRegistry: address(s_tokenAdminRegistry) - }); - EVM2EVMOnRamp.DynamicConfig memory dynamicConfig = - generateDynamicOnRampConfig(address(s_sourceRouter), address(s_feeQuoter)); - - vm.expectEmit(); - emit EVM2EVMOnRamp.ConfigSet(staticConfig, dynamicConfig); - - s_onRamp = new EVM2EVMOnRampHelper( - staticConfig, - dynamicConfig, - _getOutboundRateLimiterConfig(), - s_feeTokenConfigArgs, - s_tokenTransferFeeConfigArgs, - getNopsAndWeights() - ); - - EVM2EVMOnRamp.StaticConfig memory gotStaticConfig = s_onRamp.getStaticConfig(); - assertEq(staticConfig.linkToken, gotStaticConfig.linkToken); - assertEq(staticConfig.chainSelector, gotStaticConfig.chainSelector); - assertEq(staticConfig.destChainSelector, gotStaticConfig.destChainSelector); - assertEq(staticConfig.defaultTxGasLimit, gotStaticConfig.defaultTxGasLimit); - assertEq(staticConfig.maxNopFeesJuels, gotStaticConfig.maxNopFeesJuels); - assertEq(staticConfig.prevOnRamp, gotStaticConfig.prevOnRamp); - assertEq(staticConfig.rmnProxy, gotStaticConfig.rmnProxy); - - EVM2EVMOnRamp.DynamicConfig memory gotDynamicConfig = s_onRamp.getDynamicConfig(); - assertEq(dynamicConfig.router, gotDynamicConfig.router); - assertEq(dynamicConfig.maxNumberOfTokensPerMsg, gotDynamicConfig.maxNumberOfTokensPerMsg); - assertEq(dynamicConfig.destGasOverhead, gotDynamicConfig.destGasOverhead); - assertEq(dynamicConfig.destGasPerPayloadByte, gotDynamicConfig.destGasPerPayloadByte); - assertEq(dynamicConfig.priceRegistry, gotDynamicConfig.priceRegistry); - assertEq(dynamicConfig.maxDataBytes, gotDynamicConfig.maxDataBytes); - assertEq(dynamicConfig.maxPerMsgGasLimit, gotDynamicConfig.maxPerMsgGasLimit); - - // Initial values - assertEq("EVM2EVMOnRamp 1.5.0", s_onRamp.typeAndVersion()); - assertEq(OWNER, s_onRamp.owner()); - assertEq(1, s_onRamp.getExpectedNextSequenceNumber()); - } -} - -contract EVM2EVMOnRamp_payNops_fuzz is EVM2EVMOnRampSetup { - function test_Fuzz_NopPayNops_Success(uint96 nopFeesJuels) public { - (EVM2EVMOnRamp.NopAndWeight[] memory nopsAndWeights, uint256 weightsTotal) = s_onRamp.getNops(); - // To avoid NoFeesToPay - vm.assume(nopFeesJuels > weightsTotal); - vm.assume(nopFeesJuels < MAX_NOP_FEES_JUELS); - - // Set Nop fee juels - deal(s_sourceFeeToken, address(s_onRamp), nopFeesJuels); - vm.startPrank(address(s_sourceRouter)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, _generateEmptyMessage(), nopFeesJuels, OWNER); - - vm.startPrank(OWNER); - - uint256 totalJuels = s_onRamp.getNopFeesJuels(); - s_onRamp.payNops(); - for (uint256 i = 0; i < nopsAndWeights.length; ++i) { - uint256 expectedPayout = (totalJuels * nopsAndWeights[i].weight) / weightsTotal; - assertEq(IERC20(s_sourceFeeToken).balanceOf(nopsAndWeights[i].nop), expectedPayout); - } - } -} - -contract EVM2EVMNopsFeeSetup is EVM2EVMOnRampSetup { - function setUp() public virtual override { - EVM2EVMOnRampSetup.setUp(); - - // Since we'll mostly be testing for valid calls from the router we'll - // mock all calls to be originating from the router and re-mock in - // tests that require failure. - vm.startPrank(address(s_sourceRouter)); - - uint256 feeAmount = 1234567890; - uint256 numberOfMessages = 5; - - // Send a bunch of messages, increasing the juels in the contract - for (uint256 i = 0; i < numberOfMessages; ++i) { - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, _generateEmptyMessage(), feeAmount, OWNER); - } - - assertEq(s_onRamp.getNopFeesJuels(), feeAmount * numberOfMessages); - assertEq(IERC20(s_sourceFeeToken).balanceOf(address(s_onRamp)), feeAmount * numberOfMessages); - } -} - -contract EVM2EVMOnRamp_payNops is EVM2EVMNopsFeeSetup { - function test_OwnerPayNops_Success() public { - vm.startPrank(OWNER); - - uint256 totalJuels = s_onRamp.getNopFeesJuels(); - s_onRamp.payNops(); - (EVM2EVMOnRamp.NopAndWeight[] memory nopsAndWeights, uint256 weightsTotal) = s_onRamp.getNops(); - for (uint256 i = 0; i < nopsAndWeights.length; ++i) { - uint256 expectedPayout = (nopsAndWeights[i].weight * totalJuels) / weightsTotal; - assertEq(IERC20(s_sourceFeeToken).balanceOf(nopsAndWeights[i].nop), expectedPayout); - } - } - - function test_AdminPayNops_Success() public { - vm.startPrank(ADMIN); - - uint256 totalJuels = s_onRamp.getNopFeesJuels(); - s_onRamp.payNops(); - (EVM2EVMOnRamp.NopAndWeight[] memory nopsAndWeights, uint256 weightsTotal) = s_onRamp.getNops(); - for (uint256 i = 0; i < nopsAndWeights.length; ++i) { - uint256 expectedPayout = (nopsAndWeights[i].weight * totalJuels) / weightsTotal; - assertEq(IERC20(s_sourceFeeToken).balanceOf(nopsAndWeights[i].nop), expectedPayout); - } - } - - function test_NopPayNops_Success() public { - vm.startPrank(getNopsAndWeights()[0].nop); - - uint256 totalJuels = s_onRamp.getNopFeesJuels(); - s_onRamp.payNops(); - (EVM2EVMOnRamp.NopAndWeight[] memory nopsAndWeights, uint256 weightsTotal) = s_onRamp.getNops(); - for (uint256 i = 0; i < nopsAndWeights.length; ++i) { - uint256 expectedPayout = (nopsAndWeights[i].weight * totalJuels) / weightsTotal; - assertEq(IERC20(s_sourceFeeToken).balanceOf(nopsAndWeights[i].nop), expectedPayout); - } - } - - function test_PayNopsSuccessAfterSetNops() public { - vm.startPrank(OWNER); - - // set 2 nops, 1 from previous, 1 new - address prevNop = getNopsAndWeights()[0].nop; - address newNop = STRANGER; - EVM2EVMOnRamp.NopAndWeight[] memory nopsAndWeights = new EVM2EVMOnRamp.NopAndWeight[](2); - nopsAndWeights[0] = EVM2EVMOnRamp.NopAndWeight({nop: prevNop, weight: 1}); - nopsAndWeights[1] = EVM2EVMOnRamp.NopAndWeight({nop: newNop, weight: 1}); - s_onRamp.setNops(nopsAndWeights); - - // refill OnRamp nops fees - vm.startPrank(address(s_sourceRouter)); - uint256 feeAmount = 1234567890; - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, _generateEmptyMessage(), feeAmount, OWNER); - - vm.startPrank(newNop); - uint256 prevNopBalance = IERC20(s_sourceFeeToken).balanceOf(prevNop); - uint256 totalJuels = s_onRamp.getNopFeesJuels(); - - s_onRamp.payNops(); - - assertEq(totalJuels / 2 + prevNopBalance, IERC20(s_sourceFeeToken).balanceOf(prevNop)); - assertEq(totalJuels / 2, IERC20(s_sourceFeeToken).balanceOf(newNop)); - } - - // Reverts - - function test_InsufficientBalance_Revert() public { - vm.startPrank(address(s_onRamp)); - IERC20(s_sourceFeeToken).transfer(OWNER, IERC20(s_sourceFeeToken).balanceOf(address(s_onRamp))); - vm.startPrank(OWNER); - vm.expectRevert(EVM2EVMOnRamp.InsufficientBalance.selector); - s_onRamp.payNops(); - } - - function test_WrongPermissions_Revert() public { - vm.startPrank(STRANGER); - - vm.expectRevert(EVM2EVMOnRamp.OnlyCallableByOwnerOrAdminOrNop.selector); - s_onRamp.payNops(); - } - - function test_NoFeesToPay_Revert() public { - vm.startPrank(OWNER); - s_onRamp.payNops(); - vm.expectRevert(EVM2EVMOnRamp.NoFeesToPay.selector); - s_onRamp.payNops(); - } - - function test_NoNopsToPay_Revert() public { - vm.startPrank(OWNER); - EVM2EVMOnRamp.NopAndWeight[] memory nopsAndWeights = new EVM2EVMOnRamp.NopAndWeight[](0); - s_onRamp.setNops(nopsAndWeights); - vm.expectRevert(EVM2EVMOnRamp.NoNopsToPay.selector); - s_onRamp.payNops(); - } -} - -contract EVM2EVMOnRamp_linkAvailableForPayment is EVM2EVMNopsFeeSetup { - function test_LinkAvailableForPayment_Success() public { - uint256 totalJuels = s_onRamp.getNopFeesJuels(); - uint256 linkBalance = IERC20(s_sourceFeeToken).balanceOf(address(s_onRamp)); - - assertEq(int256(linkBalance - totalJuels), s_onRamp.linkAvailableForPayment()); - - vm.startPrank(OWNER); - s_onRamp.payNops(); - - assertEq(int256(linkBalance - totalJuels), s_onRamp.linkAvailableForPayment()); - } - - function test_InsufficientLinkBalance_Success() public { - uint256 totalJuels = s_onRamp.getNopFeesJuels(); - uint256 linkBalance = IERC20(s_sourceFeeToken).balanceOf(address(s_onRamp)); - - vm.startPrank(address(s_onRamp)); - - uint256 linkRemaining = 1; - IERC20(s_sourceFeeToken).transfer(OWNER, linkBalance - linkRemaining); - - vm.startPrank(STRANGER); - assertEq(int256(linkRemaining) - int256(totalJuels), s_onRamp.linkAvailableForPayment()); - } -} - -contract EVM2EVMOnRamp_forwardFromRouter is EVM2EVMOnRampSetup { - struct LegacyExtraArgs { - uint256 gasLimit; - bool strict; - } - - function setUp() public virtual override { - EVM2EVMOnRampSetup.setUp(); - - address[] memory feeTokens = new address[](1); - feeTokens[0] = s_sourceTokens[1]; - s_feeQuoter.applyFeeTokensUpdates(feeTokens, new address[](0)); - - // Since we'll mostly be testing for valid calls from the router we'll - // mock all calls to be originating from the router and re-mock in - // tests that require failure. - vm.startPrank(address(s_sourceRouter)); - } - - function test_ForwardFromRouterSuccessCustomExtraArgs() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT * 2})); - uint256 feeAmount = 1234567890; - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - - vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(_messageToEvent(message, 1, 1, feeAmount, OWNER)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - } - - function test_ForwardFromRouterSuccessLegacyExtraArgs() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = - abi.encodeWithSelector(Client.EVM_EXTRA_ARGS_V1_TAG, LegacyExtraArgs({gasLimit: GAS_LIMIT * 2, strict: true})); - uint256 feeAmount = 1234567890; - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - - vm.expectEmit(); - // We expect the message to be emitted with strict = false. - emit EVM2EVMOnRamp.CCIPSendRequested(_messageToEvent(message, 1, 1, feeAmount, OWNER)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - } - - function test_ForwardFromRouter_Success() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - - uint256 feeAmount = 1234567890; - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - - vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(_messageToEvent(message, 1, 1, feeAmount, OWNER)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - } - - function test_ForwardFromRouterExtraArgsV2_Success() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = abi.encodeWithSelector( - Client.EVM_EXTRA_ARGS_V2_TAG, Client.EVMExtraArgsV2({gasLimit: GAS_LIMIT * 2, allowOutOfOrderExecution: true}) - ); - uint256 feeAmount = 1234567890; - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - - vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(_messageToEvent(message, 1, 1, feeAmount, OWNER)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - } - - function test_ForwardFromRouterExtraArgsV2AllowOutOfOrderTrue_Success() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = abi.encodeWithSelector( - Client.EVM_EXTRA_ARGS_V2_TAG, Client.EVMExtraArgsV2({gasLimit: GAS_LIMIT * 2, allowOutOfOrderExecution: true}) - ); - uint256 feeAmount = 1234567890; - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - - vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(_messageToEvent(message, 1, 1, feeAmount, OWNER)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - } - - function test_Fuzz_EnforceOutOfOrder(bool enforce, bool allowOutOfOrderExecution) public { - // Update dynamic config to enforce allowOutOfOrderExecution = defaultVal. - vm.stopPrank(); - vm.startPrank(OWNER); - EVM2EVMOnRamp.DynamicConfig memory dynamicConfig = s_onRamp.getDynamicConfig(); - s_onRamp.setDynamicConfig( - EVM2EVMOnRamp.DynamicConfig({ - router: dynamicConfig.router, - maxNumberOfTokensPerMsg: dynamicConfig.maxNumberOfTokensPerMsg, - destGasOverhead: dynamicConfig.destGasOverhead, - destGasPerPayloadByte: dynamicConfig.destGasPerPayloadByte, - destDataAvailabilityOverheadGas: dynamicConfig.destDataAvailabilityOverheadGas, - destGasPerDataAvailabilityByte: dynamicConfig.destGasPerDataAvailabilityByte, - destDataAvailabilityMultiplierBps: dynamicConfig.destDataAvailabilityMultiplierBps, - priceRegistry: dynamicConfig.priceRegistry, - maxDataBytes: dynamicConfig.maxDataBytes, - maxPerMsgGasLimit: dynamicConfig.maxPerMsgGasLimit, - defaultTokenFeeUSDCents: dynamicConfig.defaultTokenFeeUSDCents, - defaultTokenDestGasOverhead: dynamicConfig.defaultTokenDestGasOverhead, - enforceOutOfOrder: enforce - }) - ); - vm.stopPrank(); - - vm.startPrank(address(s_sourceRouter)); - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = abi.encodeWithSelector( - Client.EVM_EXTRA_ARGS_V2_TAG, - Client.EVMExtraArgsV2({gasLimit: GAS_LIMIT * 2, allowOutOfOrderExecution: allowOutOfOrderExecution}) - ); - uint256 feeAmount = 1234567890; - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - - if (enforce) { - // If enforcement is on, only true should be allowed. - if (allowOutOfOrderExecution) { - vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(_messageToEvent(message, 1, 1, feeAmount, OWNER)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - } else { - vm.expectRevert(EVM2EVMOnRamp.ExtraArgOutOfOrderExecutionMustBeTrue.selector); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - } - } else { - // no enforcement should allow any value. - vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(_messageToEvent(message, 1, 1, feeAmount, OWNER)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - } - } - - function test_ShouldIncrementSeqNumAndNonce_Success() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - - for (uint64 i = 1; i < 4; ++i) { - uint64 nonceBefore = s_onRamp.getSenderNonce(OWNER); - uint64 sequenceNumberBefore = s_onRamp.getSequenceNumber(); - - vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(_messageToEvent(message, i, i, 0, OWNER)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - - uint64 nonceAfter = s_onRamp.getSenderNonce(OWNER); - uint64 sequenceNumberAfter = s_onRamp.getSequenceNumber(); - assertEq(nonceAfter, nonceBefore + 1); - assertEq(sequenceNumberAfter, sequenceNumberBefore + 1); - } - } - - function test_ShouldIncrementNonceOnlyOnOrdered_Success() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = abi.encodeWithSelector( - Client.EVM_EXTRA_ARGS_V2_TAG, Client.EVMExtraArgsV2({gasLimit: GAS_LIMIT * 2, allowOutOfOrderExecution: true}) - ); - - for (uint64 i = 1; i < 4; ++i) { - uint64 nonceBefore = s_onRamp.getSenderNonce(OWNER); - uint64 sequenceNumberBefore = s_onRamp.getSequenceNumber(); - - vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(_messageToEvent(message, i, i, 0, OWNER)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - - uint64 nonceAfter = s_onRamp.getSenderNonce(OWNER); - uint64 sequenceNumberAfter = s_onRamp.getSequenceNumber(); - assertEq(nonceAfter, nonceBefore); - assertEq(sequenceNumberAfter, sequenceNumberBefore + 1); - } - } - - function test_forwardFromRouter_ShouldStoreLinkFees_Success() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - - uint256 feeAmount = 1234567890; - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - - assertEq(IERC20(s_sourceFeeToken).balanceOf(address(s_onRamp)), feeAmount); - assertEq(s_onRamp.getNopFeesJuels(), feeAmount); - } - - function test_ShouldStoreNonLinkFees() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.feeToken = s_sourceTokens[1]; - - uint256 feeAmount = 1234567890; - IERC20(s_sourceTokens[1]).transferFrom(OWNER, address(s_onRamp), feeAmount); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - - assertEq(IERC20(s_sourceTokens[1]).balanceOf(address(s_onRamp)), feeAmount); - - // Calculate conversion done by prices contract - uint256 feeTokenPrice = s_feeQuoter.getTokenPrice(s_sourceTokens[1]).value; - uint256 linkTokenPrice = s_feeQuoter.getTokenPrice(s_sourceFeeToken).value; - uint256 conversionRate = (feeTokenPrice * 1e18) / linkTokenPrice; - uint256 expectedJuels = (feeAmount * conversionRate) / 1e18; - - assertEq(s_onRamp.getNopFeesJuels(), expectedJuels); - } - - // Make sure any valid sender, receiver and feeAmount can be handled. - // @TODO Temporarily setting lower fuzz run as 256 triggers snapshot gas off by 1 error. - // https://github.com/foundry-rs/foundry/issues/5689 - /// forge-config: default.fuzz.runs = 32 - /// forge-config: ccip.fuzz.runs = 32 - function test_Fuzz_ForwardFromRouter_Success(address originalSender, address receiver, uint96 feeTokenAmount) public { - // To avoid RouterMustSetOriginalSender - vm.assume(originalSender != address(0)); - vm.assume(uint160(receiver) >= Internal.PRECOMPILE_SPACE); - vm.assume(feeTokenAmount <= MAX_NOP_FEES_JUELS); - - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.receiver = abi.encode(receiver); - - // Make sure the tokens are in the contract - deal(s_sourceFeeToken, address(s_onRamp), feeTokenAmount); - - Internal.EVM2EVMMessage memory expectedEvent = _messageToEvent(message, 1, 1, feeTokenAmount, originalSender); - - vm.expectEmit(false, false, false, true); - emit EVM2EVMOnRamp.CCIPSendRequested(expectedEvent); - - // Assert the message Id is correct - assertEq( - expectedEvent.messageId, s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeTokenAmount, originalSender) - ); - // Assert the fee token amount is correctly assigned to the nop fee pool - assertEq(feeTokenAmount, s_onRamp.getNopFeesJuels()); - } - - function test_OverValueWithARLOff_Success() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.tokenAmounts = new Client.EVMTokenAmount[](1); - message.tokenAmounts[0].amount = 10; - message.tokenAmounts[0].token = s_sourceTokens[0]; - - IERC20(s_sourceTokens[0]).approve(address(s_onRamp), 10); - - vm.startPrank(OWNER); - // Set a high price to trip the ARL - uint224 tokenPrice = 3 ** 128; - Internal.PriceUpdates memory priceUpdates = _getSingleTokenPriceUpdateStruct(s_sourceTokens[0], tokenPrice); - s_feeQuoter.updatePrices(priceUpdates); - vm.startPrank(address(s_sourceRouter)); - - vm.expectRevert( - abi.encodeWithSelector( - RateLimiter.AggregateValueMaxCapacityExceeded.selector, - _getOutboundRateLimiterConfig().capacity, - (message.tokenAmounts[0].amount * tokenPrice) / 1e18 - ) - ); - // Expect to fail from ARL - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - - // Configure ARL off for token - EVM2EVMOnRamp.TokenTransferFeeConfig memory tokenTransferFeeConfig = - s_onRamp.getTokenTransferFeeConfig(s_sourceTokens[0]); - EVM2EVMOnRamp.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs = - new EVM2EVMOnRamp.TokenTransferFeeConfigArgs[](1); - tokenTransferFeeConfigArgs[0] = EVM2EVMOnRamp.TokenTransferFeeConfigArgs({ - token: s_sourceTokens[0], - minFeeUSDCents: tokenTransferFeeConfig.minFeeUSDCents, - maxFeeUSDCents: tokenTransferFeeConfig.maxFeeUSDCents, - deciBps: tokenTransferFeeConfig.deciBps, - destGasOverhead: tokenTransferFeeConfig.destGasOverhead, - destBytesOverhead: tokenTransferFeeConfig.destBytesOverhead, - aggregateRateLimitEnabled: false - }); - vm.startPrank(OWNER); - s_onRamp.setTokenTransferFeeConfig(tokenTransferFeeConfigArgs, new address[](0)); - - vm.startPrank(address(s_sourceRouter)); - // Expect the call now succeeds - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - } - - function test_forwardFromRouter_correctSourceTokenData_Success() public { - Client.EVM2AnyMessage memory message = _generateTokenMessage(); - - for (uint256 i = 0; i < message.tokenAmounts.length; ++i) { - address token = message.tokenAmounts[i].token; - deal(token, s_sourcePoolByToken[token], message.tokenAmounts[i].amount * 2); - } - - uint256 feeAmount = 1234567890; - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount * 2); - - Internal.EVM2EVMMessage memory expectedEvent = _messageToEvent(message, 1, 1, feeAmount, OWNER); - - vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(expectedEvent); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - - // Same message, but we change the onchain config which should be reflected in the event. - // We get the event before changing the onRamp config, as the event generation code uses the current - // onramp to generate the event. This test checks if it does so correctly. - expectedEvent = _messageToEvent(message, 2, 2, feeAmount, OWNER); - - uint256 tokenIndexToChange = 1; - address changedToken = message.tokenAmounts[tokenIndexToChange].token; - - // Set token config to change the destGasOverhead - vm.startPrank(OWNER); - EVM2EVMOnRamp.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs = - new EVM2EVMOnRamp.TokenTransferFeeConfigArgs[](1); - tokenTransferFeeConfigArgs[0] = EVM2EVMOnRamp.TokenTransferFeeConfigArgs({ - token: changedToken, - minFeeUSDCents: 0, - maxFeeUSDCents: 100, - deciBps: 0, - destGasOverhead: 1_000_111, - destBytesOverhead: uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) + 32, - aggregateRateLimitEnabled: false - }); - s_onRamp.setTokenTransferFeeConfig(tokenTransferFeeConfigArgs, new address[](0)); - - vm.startPrank(address(s_sourceRouter)); - - expectedEvent.sourceTokenData[tokenIndexToChange] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[changedToken]), - destTokenAddress: abi.encode(s_destTokenBySourceToken[changedToken]), - extraData: "", - // The user will be billed either the default or the override, so we send the exact amount that we billed for - // to the destination chain to be used for the token releaseOrMint and transfer. - destGasAmount: tokenTransferFeeConfigArgs[0].destGasOverhead - }) - ); - // Update the hash because we manually changed sourceTokenData - expectedEvent.messageId = Internal._hash(expectedEvent, s_metadataHash); - - vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(expectedEvent); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - } - - // Reverts - - function test_Paused_Revert() public { - // We pause by disabling the whitelist - vm.stopPrank(); - vm.startPrank(OWNER); - address router = address(0); - s_onRamp.setDynamicConfig(generateDynamicOnRampConfig(router, address(2))); - vm.expectRevert(EVM2EVMOnRamp.MustBeCalledByRouter.selector); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, _generateEmptyMessage(), 0, OWNER); - } - - function test_InvalidExtraArgsTag_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = bytes("bad args"); - - vm.expectRevert(EVM2EVMOnRamp.InvalidExtraArgsTag.selector); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - } - - function test_Unhealthy_Revert() public { - s_mockRMN.setGlobalCursed(true); - vm.expectRevert(EVM2EVMOnRamp.CursedByRMN.selector); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, _generateEmptyMessage(), 0, OWNER); - } - - function test_Permissions_Revert() public { - vm.stopPrank(); - vm.startPrank(OWNER); - vm.expectRevert(EVM2EVMOnRamp.MustBeCalledByRouter.selector); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, _generateEmptyMessage(), 0, OWNER); - } - - function test_OriginalSender_Revert() public { - vm.expectRevert(EVM2EVMOnRamp.RouterMustSetOriginalSender.selector); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, _generateEmptyMessage(), 0, address(0)); - } - - function test_MessageTooLarge_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.data = new bytes(MAX_DATA_SIZE + 1); - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOnRamp.MessageTooLarge.selector, MAX_DATA_SIZE, message.data.length)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, STRANGER); - } - - function test_TooManyTokens_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - uint256 tooMany = MAX_TOKENS_LENGTH + 1; - message.tokenAmounts = new Client.EVMTokenAmount[](tooMany); - vm.expectRevert(EVM2EVMOnRamp.UnsupportedNumberOfTokens.selector); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, STRANGER); - } - - function test_CannotSendZeroTokens_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.tokenAmounts = new Client.EVMTokenAmount[](1); - message.tokenAmounts[0].amount = 0; - message.tokenAmounts[0].token = s_sourceTokens[0]; - vm.expectRevert(EVM2EVMOnRamp.CannotSendZeroTokens.selector); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, STRANGER); - } - - function test_UnsupportedToken_Revert() public { - address wrongToken = address(1); - - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.tokenAmounts = new Client.EVMTokenAmount[](1); - message.tokenAmounts[0].token = wrongToken; - message.tokenAmounts[0].amount = 1; - - // We need to set the price of this new token to be able to reach - // the proper revert point. This must be called by the owner. - vm.stopPrank(); - vm.startPrank(OWNER); - - Internal.PriceUpdates memory priceUpdates = _getSingleTokenPriceUpdateStruct(wrongToken, 1); - s_feeQuoter.updatePrices(priceUpdates); - - // Change back to the router - vm.startPrank(address(s_sourceRouter)); - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOnRamp.UnsupportedToken.selector, wrongToken)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - } - - function test_MaxCapacityExceeded_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.tokenAmounts = new Client.EVMTokenAmount[](1); - message.tokenAmounts[0].amount = 2 ** 128; - message.tokenAmounts[0].token = s_sourceTokens[0]; - - IERC20(s_sourceTokens[0]).approve(address(s_onRamp), 2 ** 128); - - vm.expectRevert( - abi.encodeWithSelector( - RateLimiter.AggregateValueMaxCapacityExceeded.selector, - _getOutboundRateLimiterConfig().capacity, - (message.tokenAmounts[0].amount * s_sourceTokenPrices[0]) / 1e18 - ) - ); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - } - - function test_PriceNotFoundForToken_Revert() public { - // Set token price to 0 - vm.stopPrank(); - vm.startPrank(OWNER); - s_feeQuoter.updatePrices(_getSingleTokenPriceUpdateStruct(CUSTOM_TOKEN, 0)); - - vm.startPrank(address(s_sourceRouter)); - - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.tokenAmounts = new Client.EVMTokenAmount[](1); - message.tokenAmounts[0].token = CUSTOM_TOKEN; - message.tokenAmounts[0].amount = 1; - - vm.expectRevert(abi.encodeWithSelector(AggregateRateLimiter.PriceNotFoundForToken.selector, CUSTOM_TOKEN)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - } - - // Asserts gasLimit must be <=maxGasLimit - function test_MessageGasLimitTooHigh_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: MAX_GAS_LIMIT + 1})); - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOnRamp.MessageGasLimitTooHigh.selector)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - } - - function test_InvalidAddressEncodePacked_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.receiver = abi.encodePacked(address(234)); - - vm.expectRevert(abi.encodeWithSelector(Internal.InvalidEVMAddress.selector, message.receiver)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 1, OWNER); - } - - function test_InvalidAddress_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.receiver = abi.encode(type(uint208).max); - - vm.expectRevert(abi.encodeWithSelector(Internal.InvalidEVMAddress.selector, message.receiver)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 1, OWNER); - } - - // We disallow sending to addresses 0-9. - function test_ZeroAddressReceiver_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - - for (uint160 i = 0; i < 10; ++i) { - message.receiver = abi.encode(address(i)); - - vm.expectRevert(abi.encodeWithSelector(Internal.InvalidEVMAddress.selector, message.receiver)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 1, OWNER); - } - } - - function test_MaxFeeBalanceReached_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - - vm.expectRevert(EVM2EVMOnRamp.MaxFeeBalanceReached.selector); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, MAX_NOP_FEES_JUELS + 1, OWNER); - } - - function test_InvalidChainSelector_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - - uint64 wrongChainSelector = DEST_CHAIN_SELECTOR + 1; - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOnRamp.InvalidChainSelector.selector, wrongChainSelector)); - - s_onRamp.forwardFromRouter(wrongChainSelector, message, 1, OWNER); - } - - function test_SourceTokenDataTooLarge_Revert() public { - address sourceETH = s_sourceTokens[1]; - vm.stopPrank(); - vm.startPrank(OWNER); - - MaybeRevertingBurnMintTokenPool newPool = new MaybeRevertingBurnMintTokenPool( - BurnMintERC677(sourceETH), new address[](0), address(s_mockRMN), address(s_sourceRouter) - ); - BurnMintERC677(sourceETH).grantMintAndBurnRoles(address(newPool)); - deal(address(sourceETH), address(newPool), type(uint256).max); - - // Add TokenPool to OnRamp - s_tokenAdminRegistry.setPool(sourceETH, address(newPool)); - - // Allow chain in TokenPool - TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](1); - chainUpdates[0] = TokenPool.ChainUpdate({ - remoteChainSelector: DEST_CHAIN_SELECTOR, - remotePoolAddress: abi.encode(s_destTokenPool), - remoteTokenAddress: abi.encode(s_destToken), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - newPool.applyChainUpdates(chainUpdates); - - Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(address(sourceETH), 1000); - - // No data set, should succeed - vm.startPrank(address(s_sourceRouter)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - - // Set max data length, should succeed - vm.startPrank(OWNER); - newPool.setSourceTokenData(new bytes(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES)); - - vm.startPrank(address(s_sourceRouter)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - - // Set data to max length +1, should revert - vm.startPrank(OWNER); - newPool.setSourceTokenData(new bytes(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES + 1)); - - vm.startPrank(address(s_sourceRouter)); - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOnRamp.SourceTokenDataTooLarge.selector, sourceETH)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - - // Set token config to allow larger data - vm.startPrank(OWNER); - EVM2EVMOnRamp.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs = - new EVM2EVMOnRamp.TokenTransferFeeConfigArgs[](1); - tokenTransferFeeConfigArgs[0] = EVM2EVMOnRamp.TokenTransferFeeConfigArgs({ - token: sourceETH, - minFeeUSDCents: 1, - maxFeeUSDCents: 0, - deciBps: 0, - destGasOverhead: 0, - destBytesOverhead: uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) + 32, - aggregateRateLimitEnabled: false - }); - s_onRamp.setTokenTransferFeeConfig(tokenTransferFeeConfigArgs, new address[](0)); - - vm.startPrank(address(s_sourceRouter)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - - // Set the token data larger than the configured token data, should revert - vm.startPrank(OWNER); - newPool.setSourceTokenData(new bytes(uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) + 32 + 1)); - - vm.startPrank(address(s_sourceRouter)); - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOnRamp.SourceTokenDataTooLarge.selector, sourceETH)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - } - - function test_forwardFromRouter_UnsupportedToken_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.tokenAmounts = new Client.EVMTokenAmount[](1); - message.tokenAmounts[0].amount = 1; - message.tokenAmounts[0].token = address(1); - - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOnRamp.UnsupportedToken.selector, message.tokenAmounts[0].token)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - } - - function test_EnforceOutOfOrder_Revert() public { - // Update dynamic config to enforce allowOutOfOrderExecution = true. - vm.stopPrank(); - vm.startPrank(OWNER); - EVM2EVMOnRamp.DynamicConfig memory dynamicConfig = s_onRamp.getDynamicConfig(); - s_onRamp.setDynamicConfig( - EVM2EVMOnRamp.DynamicConfig({ - router: dynamicConfig.router, - maxNumberOfTokensPerMsg: dynamicConfig.maxNumberOfTokensPerMsg, - destGasOverhead: dynamicConfig.destGasOverhead, - destGasPerPayloadByte: dynamicConfig.destGasPerPayloadByte, - destDataAvailabilityOverheadGas: dynamicConfig.destDataAvailabilityOverheadGas, - destGasPerDataAvailabilityByte: dynamicConfig.destGasPerDataAvailabilityByte, - destDataAvailabilityMultiplierBps: dynamicConfig.destDataAvailabilityMultiplierBps, - priceRegistry: dynamicConfig.priceRegistry, - maxDataBytes: dynamicConfig.maxDataBytes, - maxPerMsgGasLimit: dynamicConfig.maxPerMsgGasLimit, - defaultTokenFeeUSDCents: dynamicConfig.defaultTokenFeeUSDCents, - defaultTokenDestGasOverhead: dynamicConfig.defaultTokenDestGasOverhead, - enforceOutOfOrder: true - }) - ); - vm.stopPrank(); - - vm.startPrank(address(s_sourceRouter)); - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - // Empty extraArgs to should revert since it enforceOutOfOrder is true. - message.extraArgs = ""; - uint256 feeAmount = 1234567890; - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - - vm.expectRevert(EVM2EVMOnRamp.ExtraArgOutOfOrderExecutionMustBeTrue.selector); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - } -} - -contract EVM2EVMOnRamp_forwardFromRouter_upgrade is EVM2EVMOnRampSetup { - uint256 internal constant FEE_AMOUNT = 1234567890; - EVM2EVMOnRampHelper internal s_prevOnRamp; - - function setUp() public virtual override { - EVM2EVMOnRampSetup.setUp(); - - s_prevOnRamp = s_onRamp; - - s_onRamp = new EVM2EVMOnRampHelper( - EVM2EVMOnRamp.StaticConfig({ - linkToken: s_sourceTokens[0], - chainSelector: SOURCE_CHAIN_SELECTOR, - destChainSelector: DEST_CHAIN_SELECTOR, - defaultTxGasLimit: GAS_LIMIT, - maxNopFeesJuels: MAX_NOP_FEES_JUELS, - prevOnRamp: address(s_prevOnRamp), - rmnProxy: address(s_mockRMN), - tokenAdminRegistry: address(s_tokenAdminRegistry) - }), - generateDynamicOnRampConfig(address(s_sourceRouter), address(s_feeQuoter)), - _getOutboundRateLimiterConfig(), - s_feeTokenConfigArgs, - s_tokenTransferFeeConfigArgs, - getNopsAndWeights() - ); - s_onRamp.setAdmin(ADMIN); - - s_metadataHash = keccak256( - abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, SOURCE_CHAIN_SELECTOR, DEST_CHAIN_SELECTOR, address(s_onRamp)) - ); - - vm.startPrank(address(s_sourceRouter)); - } - - function test_V2_Success() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - - vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(_messageToEvent(message, 1, 1, FEE_AMOUNT, OWNER)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, FEE_AMOUNT, OWNER); - } - - function test_V2SenderNoncesReadsPreviousRamp_Success() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - uint64 startNonce = s_onRamp.getSenderNonce(OWNER); - - for (uint64 i = 1; i < 4; ++i) { - s_prevOnRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - - assertEq(startNonce + i, s_onRamp.getSenderNonce(OWNER)); - } - } - - function test_V2NonceStartsAtV1Nonce_Success() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - - uint64 startNonce = s_onRamp.getSenderNonce(OWNER); - - // send 1 message from previous onramp - s_prevOnRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, FEE_AMOUNT, OWNER); - - assertEq(startNonce + 1, s_onRamp.getSenderNonce(OWNER)); - - // new onramp nonce should start from 2, while sequence number start from 1 - vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(_messageToEvent(message, 1, startNonce + 2, FEE_AMOUNT, OWNER)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, FEE_AMOUNT, OWNER); - - assertEq(startNonce + 2, s_onRamp.getSenderNonce(OWNER)); - - // after another send, nonce should be 3, and sequence number be 2 - vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(_messageToEvent(message, 2, startNonce + 3, FEE_AMOUNT, OWNER)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, FEE_AMOUNT, OWNER); - - assertEq(startNonce + 3, s_onRamp.getSenderNonce(OWNER)); - } - - function test_V2NonceNewSenderStartsAtZero_Success() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - - // send 1 message from previous onramp from OWNER - s_prevOnRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, FEE_AMOUNT, OWNER); - - address newSender = address(1234567); - // new onramp nonce should start from 1 for new sender - vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(_messageToEvent(message, 1, 1, FEE_AMOUNT, newSender)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, FEE_AMOUNT, newSender); - } -} - -contract EVM2EVMOnRamp_getFeeSetup is EVM2EVMOnRampSetup { - uint224 internal s_feeTokenPrice; - uint224 internal s_wrappedTokenPrice; - uint224 internal s_customTokenPrice; - - address internal s_selfServeTokenDefaultPricing = makeAddr("self-serve-token-default-pricing"); - - function setUp() public virtual override { - EVM2EVMOnRampSetup.setUp(); - - // Add additional pool addresses for test tokens to mark them as supported - s_tokenAdminRegistry.proposeAdministrator(s_sourceRouter.getWrappedNative(), OWNER); - s_tokenAdminRegistry.acceptAdminRole(s_sourceRouter.getWrappedNative()); - s_tokenAdminRegistry.proposeAdministrator(CUSTOM_TOKEN, OWNER); - s_tokenAdminRegistry.acceptAdminRole(CUSTOM_TOKEN); - - LockReleaseTokenPool wrappedNativePool = new LockReleaseTokenPool( - IERC20(s_sourceRouter.getWrappedNative()), new address[](0), address(s_mockRMN), true, address(s_sourceRouter) - ); - - TokenPool.ChainUpdate[] memory wrappedNativeChainUpdate = new TokenPool.ChainUpdate[](1); - wrappedNativeChainUpdate[0] = TokenPool.ChainUpdate({ - remoteChainSelector: DEST_CHAIN_SELECTOR, - remotePoolAddress: abi.encode(address(111111)), - remoteTokenAddress: abi.encode(s_destToken), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - wrappedNativePool.applyChainUpdates(wrappedNativeChainUpdate); - s_tokenAdminRegistry.setPool(s_sourceRouter.getWrappedNative(), address(wrappedNativePool)); - - LockReleaseTokenPool customPool = new LockReleaseTokenPool( - IERC20(CUSTOM_TOKEN), new address[](0), address(s_mockRMN), true, address(s_sourceRouter) - ); - TokenPool.ChainUpdate[] memory customChainUpdate = new TokenPool.ChainUpdate[](1); - customChainUpdate[0] = TokenPool.ChainUpdate({ - remoteChainSelector: DEST_CHAIN_SELECTOR, - remotePoolAddress: abi.encode(makeAddr("random")), - remoteTokenAddress: abi.encode(s_destToken), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - customPool.applyChainUpdates(customChainUpdate); - s_tokenAdminRegistry.setPool(CUSTOM_TOKEN, address(customPool)); - - s_feeTokenPrice = s_sourceTokenPrices[0]; - s_wrappedTokenPrice = s_sourceTokenPrices[2]; - s_customTokenPrice = CUSTOM_TOKEN_PRICE; - - // Ensure the self-serve token is set up on the admin registry - vm.mockCall( - address(s_tokenAdminRegistry), - abi.encodeWithSelector(ITokenAdminRegistry.getPool.selector, s_selfServeTokenDefaultPricing), - abi.encode(makeAddr("self-serve-pool")) - ); - } - - function calcUSDValueFromTokenAmount(uint224 tokenPrice, uint256 tokenAmount) internal pure returns (uint256) { - return (tokenPrice * tokenAmount) / 1e18; - } - - function applyBpsRatio(uint256 tokenAmount, uint16 ratio) internal pure returns (uint256) { - return (tokenAmount * ratio) / 1e5; - } - - function configUSDCentToWei(uint256 usdCent) internal pure returns (uint256) { - return usdCent * 1e16; - } -} - -contract EVM2EVMOnRamp_getDataAvailabilityCost is EVM2EVMOnRamp_getFeeSetup { - function test_EmptyMessageCalculatesDataAvailabilityCost_Success() public view { - uint256 dataAvailabilityCostUSD = s_onRamp.getDataAvailabilityCost(USD_PER_DATA_AVAILABILITY_GAS, 0, 0, 0); - - EVM2EVMOnRamp.DynamicConfig memory dynamicConfig = s_onRamp.getDynamicConfig(); - - uint256 dataAvailabilityGas = dynamicConfig.destDataAvailabilityOverheadGas - + dynamicConfig.destGasPerDataAvailabilityByte * Internal.MESSAGE_FIXED_BYTES; - uint256 expectedDataAvailabilityCostUSD = - USD_PER_DATA_AVAILABILITY_GAS * dataAvailabilityGas * dynamicConfig.destDataAvailabilityMultiplierBps * 1e14; - - assertEq(expectedDataAvailabilityCostUSD, dataAvailabilityCostUSD); - } - - function test_SimpleMessageCalculatesDataAvailabilityCost_Success() public view { - uint256 dataAvailabilityCostUSD = s_onRamp.getDataAvailabilityCost(USD_PER_DATA_AVAILABILITY_GAS, 100, 5, 50); - - EVM2EVMOnRamp.DynamicConfig memory dynamicConfig = s_onRamp.getDynamicConfig(); - - uint256 dataAvailabilityLengthBytes = - Internal.MESSAGE_FIXED_BYTES + 100 + (5 * Internal.MESSAGE_FIXED_BYTES_PER_TOKEN) + 50; - uint256 dataAvailabilityGas = dynamicConfig.destDataAvailabilityOverheadGas - + dynamicConfig.destGasPerDataAvailabilityByte * dataAvailabilityLengthBytes; - uint256 expectedDataAvailabilityCostUSD = - USD_PER_DATA_AVAILABILITY_GAS * dataAvailabilityGas * dynamicConfig.destDataAvailabilityMultiplierBps * 1e14; - - assertEq(expectedDataAvailabilityCostUSD, dataAvailabilityCostUSD); - } - - function test_Fuzz_ZeroDataAvailabilityGasPriceAlwaysCalculatesZeroDataAvailabilityCost_Success( - uint64 messageDataLength, - uint32 numberOfTokens, - uint32 tokenTransferBytesOverhead - ) public view { - uint256 dataAvailabilityCostUSD = - s_onRamp.getDataAvailabilityCost(0, messageDataLength, numberOfTokens, tokenTransferBytesOverhead); - - assertEq(0, dataAvailabilityCostUSD); - } - - function test_Fuzz_CalculateDataAvailabilityCost_Success( - uint32 destDataAvailabilityOverheadGas, - uint16 destGasPerDataAvailabilityByte, - uint16 destDataAvailabilityMultiplierBps, - uint112 dataAvailabilityGasPrice, - uint64 messageDataLength, - uint32 numberOfTokens, - uint32 tokenTransferBytesOverhead - ) public { - EVM2EVMOnRamp.DynamicConfig memory dynamicConfig = s_onRamp.getDynamicConfig(); - dynamicConfig.destDataAvailabilityOverheadGas = destDataAvailabilityOverheadGas; - dynamicConfig.destGasPerDataAvailabilityByte = destGasPerDataAvailabilityByte; - dynamicConfig.destDataAvailabilityMultiplierBps = destDataAvailabilityMultiplierBps; - s_onRamp.setDynamicConfig(dynamicConfig); - - uint256 dataAvailabilityCostUSD = s_onRamp.getDataAvailabilityCost( - dataAvailabilityGasPrice, messageDataLength, numberOfTokens, tokenTransferBytesOverhead - ); - - uint256 dataAvailabilityLengthBytes = Internal.MESSAGE_FIXED_BYTES + messageDataLength - + (numberOfTokens * Internal.MESSAGE_FIXED_BYTES_PER_TOKEN) + tokenTransferBytesOverhead; - - uint256 dataAvailabilityGas = - destDataAvailabilityOverheadGas + destGasPerDataAvailabilityByte * dataAvailabilityLengthBytes; - uint256 expectedDataAvailabilityCostUSD = - dataAvailabilityGasPrice * dataAvailabilityGas * destDataAvailabilityMultiplierBps * 1e14; - - assertEq(expectedDataAvailabilityCostUSD, dataAvailabilityCostUSD); - } -} - -contract EVM2EVMOnRamp_getSupportedTokens is EVM2EVMOnRampSetup { - function test_GetSupportedTokens_Revert() public { - vm.expectRevert(EVM2EVMOnRamp.GetSupportedTokensFunctionalityRemovedCheckAdminRegistry.selector); - s_onRamp.getSupportedTokens(DEST_CHAIN_SELECTOR); - } -} - -contract EVM2EVMOnRamp_getTokenTransferCost is EVM2EVMOnRamp_getFeeSetup { - using USDPriceWith18Decimals for uint224; - - function test_NoTokenTransferChargesZeroFee_Success() public view { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = - s_onRamp.getTokenTransferCost(message.feeToken, s_feeTokenPrice, message.tokenAmounts); - - assertEq(0, feeUSDWei); - assertEq(0, destGasOverhead); - assertEq(0, destBytesOverhead); - } - - function test__getTokenTransferCost_selfServeUsesDefaults_Success() public view { - Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_selfServeTokenDefaultPricing, 1000); - - // Get config to assert it isn't set - EVM2EVMOnRamp.TokenTransferFeeConfig memory transferFeeConfig = - s_onRamp.getTokenTransferFeeConfig(message.tokenAmounts[0].token); - - assertFalse(transferFeeConfig.isEnabled); - - (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = - s_onRamp.getTokenTransferCost(message.feeToken, s_feeTokenPrice, message.tokenAmounts); - - // Assert that the default values are used - assertEq(uint256(DEFAULT_TOKEN_FEE_USD_CENTS) * 1e16, feeUSDWei); - assertEq(DEFAULT_TOKEN_DEST_GAS_OVERHEAD, destGasOverhead); - assertEq(DEFAULT_TOKEN_BYTES_OVERHEAD, destBytesOverhead); - } - - function test_SmallTokenTransferChargesMinFeeAndGas_Success() public view { - Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_sourceFeeToken, 1000); - EVM2EVMOnRamp.TokenTransferFeeConfig memory transferFeeConfig = - s_onRamp.getTokenTransferFeeConfig(message.tokenAmounts[0].token); - - (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = - s_onRamp.getTokenTransferCost(message.feeToken, s_feeTokenPrice, message.tokenAmounts); - - assertEq(configUSDCentToWei(transferFeeConfig.minFeeUSDCents), feeUSDWei); - assertEq(transferFeeConfig.destGasOverhead, destGasOverhead); - assertEq(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES, destBytesOverhead); - } - - function test_ZeroAmountTokenTransferChargesMinFeeAndGas_Success() public view { - Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_sourceFeeToken, 0); - EVM2EVMOnRamp.TokenTransferFeeConfig memory transferFeeConfig = - s_onRamp.getTokenTransferFeeConfig(message.tokenAmounts[0].token); - - (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = - s_onRamp.getTokenTransferCost(message.feeToken, s_feeTokenPrice, message.tokenAmounts); - - assertEq(configUSDCentToWei(transferFeeConfig.minFeeUSDCents), feeUSDWei); - assertEq(transferFeeConfig.destGasOverhead, destGasOverhead); - assertEq(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES, destBytesOverhead); - } - - function test_LargeTokenTransferChargesMaxFeeAndGas_Success() public view { - Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_sourceFeeToken, 1e36); - EVM2EVMOnRamp.TokenTransferFeeConfig memory transferFeeConfig = - s_onRamp.getTokenTransferFeeConfig(message.tokenAmounts[0].token); - - (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = - s_onRamp.getTokenTransferCost(message.feeToken, s_feeTokenPrice, message.tokenAmounts); - - assertEq(configUSDCentToWei(transferFeeConfig.maxFeeUSDCents), feeUSDWei); - assertEq(transferFeeConfig.destGasOverhead, destGasOverhead); - assertEq(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES, destBytesOverhead); - } - - function test_FeeTokenBpsFee_Success() public view { - uint256 tokenAmount = 10000e18; - - Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_sourceFeeToken, tokenAmount); - EVM2EVMOnRamp.TokenTransferFeeConfig memory transferFeeConfig = - s_onRamp.getTokenTransferFeeConfig(message.tokenAmounts[0].token); - - (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = - s_onRamp.getTokenTransferCost(message.feeToken, s_feeTokenPrice, message.tokenAmounts); - - uint256 usdWei = calcUSDValueFromTokenAmount(s_feeTokenPrice, tokenAmount); - uint256 bpsUSDWei = applyBpsRatio(usdWei, s_tokenTransferFeeConfigArgs[0].deciBps); - - assertEq(bpsUSDWei, feeUSDWei); - assertEq(transferFeeConfig.destGasOverhead, destGasOverhead); - assertEq(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES, destBytesOverhead); - } - - function test_CustomTokenBpsFee_Success() public view { - uint256 tokenAmount = 200000e18; - - Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ - receiver: abi.encode(OWNER), - data: "", - tokenAmounts: new Client.EVMTokenAmount[](1), - feeToken: s_sourceFeeToken, - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT})) - }); - message.tokenAmounts[0] = Client.EVMTokenAmount({token: CUSTOM_TOKEN, amount: tokenAmount}); - - EVM2EVMOnRamp.TokenTransferFeeConfig memory transferFeeConfig = - s_onRamp.getTokenTransferFeeConfig(message.tokenAmounts[0].token); - - (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = - s_onRamp.getTokenTransferCost(message.feeToken, s_feeTokenPrice, message.tokenAmounts); - - uint256 usdWei = calcUSDValueFromTokenAmount(s_customTokenPrice, tokenAmount); - uint256 bpsUSDWei = applyBpsRatio(usdWei, s_tokenTransferFeeConfigArgs[1].deciBps); - - assertEq(bpsUSDWei, feeUSDWei); - assertEq(transferFeeConfig.destGasOverhead, destGasOverhead); - assertEq(transferFeeConfig.destBytesOverhead, destBytesOverhead); - } - - function test_ZeroFeeConfigChargesMinFee_Success() public { - EVM2EVMOnRamp.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs = - new EVM2EVMOnRamp.TokenTransferFeeConfigArgs[](1); - tokenTransferFeeConfigArgs[0] = EVM2EVMOnRamp.TokenTransferFeeConfigArgs({ - token: s_sourceFeeToken, - minFeeUSDCents: 1, - maxFeeUSDCents: 0, - deciBps: 0, - destGasOverhead: 0, - destBytesOverhead: uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES), - aggregateRateLimitEnabled: true - }); - s_onRamp.setTokenTransferFeeConfig(tokenTransferFeeConfigArgs, new address[](0)); - - Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_sourceFeeToken, 1e36); - (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = - s_onRamp.getTokenTransferCost(message.feeToken, s_feeTokenPrice, message.tokenAmounts); - - // if token charges 0 bps, it should cost minFee to transfer - assertEq(configUSDCentToWei(tokenTransferFeeConfigArgs[0].minFeeUSDCents), feeUSDWei); - assertEq(0, destGasOverhead); - assertEq(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES, destBytesOverhead); - } - - function test_Fuzz_TokenTransferFeeDuplicateTokens_Success(uint256 transfers, uint256 amount) public view { - // It shouldn't be possible to pay materially lower fees by splitting up the transfers. - // Note it is possible to pay higher fees since the minimum fees are added. - EVM2EVMOnRamp.DynamicConfig memory dynamicConfig = s_onRamp.getDynamicConfig(); - transfers = bound(transfers, 1, dynamicConfig.maxNumberOfTokensPerMsg); - // Cap amount to avoid overflow - amount = bound(amount, 0, 1e36); - Client.EVMTokenAmount[] memory multiple = new Client.EVMTokenAmount[](transfers); - for (uint256 i = 0; i < transfers; ++i) { - multiple[i] = Client.EVMTokenAmount({token: s_sourceTokens[0], amount: amount}); - } - Client.EVMTokenAmount[] memory single = new Client.EVMTokenAmount[](1); - single[0] = Client.EVMTokenAmount({token: s_sourceTokens[0], amount: amount * transfers}); - - address feeToken = s_sourceRouter.getWrappedNative(); - - (uint256 feeSingleUSDWei, uint32 gasOverheadSingle, uint32 bytesOverheadSingle) = - s_onRamp.getTokenTransferCost(feeToken, s_wrappedTokenPrice, single); - (uint256 feeMultipleUSDWei, uint32 gasOverheadMultiple, uint32 bytesOverheadMultiple) = - s_onRamp.getTokenTransferCost(feeToken, s_wrappedTokenPrice, multiple); - - // Note that there can be a rounding error once per split. - assertTrue(feeMultipleUSDWei >= (feeSingleUSDWei - dynamicConfig.maxNumberOfTokensPerMsg)); - assertEq(gasOverheadMultiple, gasOverheadSingle * transfers); - assertEq(bytesOverheadMultiple, bytesOverheadSingle * transfers); - } - - function test_MixedTokenTransferFee_Success() public view { - address[3] memory testTokens = [s_sourceFeeToken, s_sourceRouter.getWrappedNative(), CUSTOM_TOKEN]; - uint224[3] memory tokenPrices = [s_feeTokenPrice, s_wrappedTokenPrice, s_customTokenPrice]; - EVM2EVMOnRamp.TokenTransferFeeConfig[3] memory tokenTransferFeeConfigs = [ - s_onRamp.getTokenTransferFeeConfig(testTokens[0]), - s_onRamp.getTokenTransferFeeConfig(testTokens[1]), - s_onRamp.getTokenTransferFeeConfig(testTokens[2]) - ]; - - Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ - receiver: abi.encode(OWNER), - data: "", - tokenAmounts: new Client.EVMTokenAmount[](3), - feeToken: s_sourceRouter.getWrappedNative(), - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT})) - }); - uint256 expectedTotalGas = 0; - uint256 expectedTotalBytes = 0; - - // Start with small token transfers, total bps fee is lower than min token transfer fee - for (uint256 i = 0; i < testTokens.length; ++i) { - message.tokenAmounts[i] = Client.EVMTokenAmount({token: testTokens[i], amount: 1e14}); - EVM2EVMOnRamp.TokenTransferFeeConfig memory tokenTransferFeeConfig = - s_onRamp.getTokenTransferFeeConfig(testTokens[i]); - expectedTotalGas += tokenTransferFeeConfig.destGasOverhead == 0 - ? DEFAULT_TOKEN_DEST_GAS_OVERHEAD - : tokenTransferFeeConfig.destGasOverhead; - expectedTotalBytes += tokenTransferFeeConfig.destBytesOverhead == 0 - ? uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) - : tokenTransferFeeConfig.destBytesOverhead; - } - (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = - s_onRamp.getTokenTransferCost(message.feeToken, s_wrappedTokenPrice, message.tokenAmounts); - - uint256 expectedFeeUSDWei = 0; - for (uint256 i = 0; i < testTokens.length; ++i) { - expectedFeeUSDWei += configUSDCentToWei( - tokenTransferFeeConfigs[i].minFeeUSDCents == 0 - ? DEFAULT_TOKEN_FEE_USD_CENTS - : tokenTransferFeeConfigs[i].minFeeUSDCents - ); - } - - assertEq(expectedFeeUSDWei, feeUSDWei, "wrong feeUSDWei 1"); - assertEq(expectedTotalGas, destGasOverhead, "wrong destGasOverhead 1"); - assertEq(expectedTotalBytes, destBytesOverhead, "wrong destBytesOverhead 1"); - - // Set 1st token transfer to a meaningful amount so its bps fee is now between min and max fee - message.tokenAmounts[0] = Client.EVMTokenAmount({token: testTokens[0], amount: 10000e18}); - - uint256 token0USDWei = applyBpsRatio( - calcUSDValueFromTokenAmount(tokenPrices[0], message.tokenAmounts[0].amount), tokenTransferFeeConfigs[0].deciBps - ); - uint256 token1USDWei = configUSDCentToWei(DEFAULT_TOKEN_FEE_USD_CENTS); - - (feeUSDWei, destGasOverhead, destBytesOverhead) = - s_onRamp.getTokenTransferCost(message.feeToken, s_wrappedTokenPrice, message.tokenAmounts); - expectedFeeUSDWei = token0USDWei + token1USDWei + configUSDCentToWei(tokenTransferFeeConfigs[2].minFeeUSDCents); - - assertEq(expectedFeeUSDWei, feeUSDWei, "wrong feeUSDWei 2"); - assertEq(expectedTotalGas, destGasOverhead, "wrong destGasOverhead 2"); - assertEq(expectedTotalBytes, destBytesOverhead, "wrong destBytesOverhead 2"); - - // Set 2nd token transfer to a large amount that is higher than maxFeeUSD - message.tokenAmounts[2] = Client.EVMTokenAmount({token: testTokens[2], amount: 1e36}); - - (feeUSDWei, destGasOverhead, destBytesOverhead) = - s_onRamp.getTokenTransferCost(message.feeToken, s_wrappedTokenPrice, message.tokenAmounts); - expectedFeeUSDWei = token0USDWei + token1USDWei + configUSDCentToWei(tokenTransferFeeConfigs[2].maxFeeUSDCents); - - assertEq(expectedFeeUSDWei, feeUSDWei, "wrong feeUSDWei 3"); - assertEq(expectedTotalGas, destGasOverhead, "wrong destGasOverhead 3"); - assertEq(expectedTotalBytes, destBytesOverhead, "wrong destBytesOverhead 3"); - } - - // reverts - - function test_UnsupportedToken_Revert() public { - address NOT_SUPPORTED_TOKEN = address(123); - Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(NOT_SUPPORTED_TOKEN, 200); - - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOnRamp.UnsupportedToken.selector, NOT_SUPPORTED_TOKEN)); - - s_onRamp.getTokenTransferCost(message.feeToken, s_feeTokenPrice, message.tokenAmounts); - } -} - -contract EVM2EVMOnRamp_getFee is EVM2EVMOnRamp_getFeeSetup { - using USDPriceWith18Decimals for uint224; - - function test_EmptyMessage_Success() public view { - address[2] memory testTokens = [s_sourceFeeToken, s_sourceRouter.getWrappedNative()]; - uint224[2] memory feeTokenPrices = [s_feeTokenPrice, s_wrappedTokenPrice]; - - for (uint256 i = 0; i < feeTokenPrices.length; ++i) { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.feeToken = testTokens[i]; - EVM2EVMOnRamp.FeeTokenConfig memory feeTokenConfig = s_onRamp.getFeeTokenConfig(message.feeToken); - - uint256 feeAmount = s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); - - uint256 gasUsed = GAS_LIMIT + DEST_GAS_OVERHEAD; - uint256 gasFeeUSD = (gasUsed * feeTokenConfig.gasMultiplierWeiPerEth * USD_PER_GAS); - uint256 messageFeeUSD = - (configUSDCentToWei(feeTokenConfig.networkFeeUSDCents) * feeTokenConfig.premiumMultiplierWeiPerEth); - uint256 dataAvailabilityFeeUSD = s_onRamp.getDataAvailabilityCost( - USD_PER_DATA_AVAILABILITY_GAS, message.data.length, message.tokenAmounts.length, 0 - ); - - uint256 totalPriceInFeeToken = (gasFeeUSD + messageFeeUSD + dataAvailabilityFeeUSD) / feeTokenPrices[i]; - assertEq(totalPriceInFeeToken, feeAmount); - } - } - - function test_GetFeeOfZeroForTokenMessage_Success() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - - uint256 feeAmount = s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); - assertTrue(feeAmount > 0); - - EVM2EVMOnRamp.FeeTokenConfigArgs[] memory feeTokenConfigArgs = new EVM2EVMOnRamp.FeeTokenConfigArgs[](1); - feeTokenConfigArgs[0] = EVM2EVMOnRamp.FeeTokenConfigArgs({ - token: message.feeToken, - networkFeeUSDCents: 0, - gasMultiplierWeiPerEth: 0, - premiumMultiplierWeiPerEth: 0, - enabled: true - }); - - s_onRamp.setFeeTokenConfig(feeTokenConfigArgs); - EVM2EVMOnRamp.DynamicConfig memory config = - generateDynamicOnRampConfig(address(s_sourceRouter), address(s_feeQuoter)); - config.destDataAvailabilityMultiplierBps = 0; - - s_onRamp.setDynamicConfig(config); - - feeAmount = s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); - assertEq(0, feeAmount); - } - - function test_ZeroDataAvailabilityMultiplier_Success() public { - EVM2EVMOnRamp.DynamicConfig memory dynamicConfig = s_onRamp.getDynamicConfig(); - dynamicConfig.destDataAvailabilityMultiplierBps = 0; - s_onRamp.setDynamicConfig(dynamicConfig); - - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - EVM2EVMOnRamp.FeeTokenConfig memory feeTokenConfig = s_onRamp.getFeeTokenConfig(message.feeToken); - - uint256 feeAmount = s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); - - uint256 gasUsed = GAS_LIMIT + DEST_GAS_OVERHEAD; - uint256 gasFeeUSD = (gasUsed * feeTokenConfig.gasMultiplierWeiPerEth * USD_PER_GAS); - uint256 messageFeeUSD = - (configUSDCentToWei(feeTokenConfig.networkFeeUSDCents) * feeTokenConfig.premiumMultiplierWeiPerEth); - - uint256 totalPriceInFeeToken = (gasFeeUSD + messageFeeUSD) / s_feeTokenPrice; - assertEq(totalPriceInFeeToken, feeAmount); - } - - function test_HighGasMessage_Success() public view { - address[2] memory testTokens = [s_sourceFeeToken, s_sourceRouter.getWrappedNative()]; - uint224[2] memory feeTokenPrices = [s_feeTokenPrice, s_wrappedTokenPrice]; - - uint256 customGasLimit = MAX_GAS_LIMIT; - uint256 customDataSize = MAX_DATA_SIZE; - for (uint256 i = 0; i < feeTokenPrices.length; ++i) { - Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ - receiver: abi.encode(OWNER), - data: new bytes(customDataSize), - tokenAmounts: new Client.EVMTokenAmount[](0), - feeToken: testTokens[i], - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: customGasLimit})) - }); - - EVM2EVMOnRamp.FeeTokenConfig memory feeTokenConfig = s_onRamp.getFeeTokenConfig(message.feeToken); - uint256 feeAmount = s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); - - uint256 gasUsed = customGasLimit + DEST_GAS_OVERHEAD + customDataSize * DEST_GAS_PER_PAYLOAD_BYTE; - uint256 gasFeeUSD = (gasUsed * feeTokenConfig.gasMultiplierWeiPerEth * USD_PER_GAS); - uint256 messageFeeUSD = - (configUSDCentToWei(feeTokenConfig.networkFeeUSDCents) * feeTokenConfig.premiumMultiplierWeiPerEth); - uint256 dataAvailabilityFeeUSD = s_onRamp.getDataAvailabilityCost( - USD_PER_DATA_AVAILABILITY_GAS, message.data.length, message.tokenAmounts.length, 0 - ); - - uint256 totalPriceInFeeToken = (gasFeeUSD + messageFeeUSD + dataAvailabilityFeeUSD) / feeTokenPrices[i]; - assertEq(totalPriceInFeeToken, feeAmount); - } - } - - function test_SingleTokenMessage_Success() public view { - address[2] memory testTokens = [s_sourceFeeToken, s_sourceRouter.getWrappedNative()]; - uint224[2] memory feeTokenPrices = [s_feeTokenPrice, s_wrappedTokenPrice]; - - uint256 tokenAmount = 10000e18; - for (uint256 i = 0; i < feeTokenPrices.length; ++i) { - Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_sourceFeeToken, tokenAmount); - message.feeToken = testTokens[i]; - EVM2EVMOnRamp.FeeTokenConfig memory feeTokenConfig = s_onRamp.getFeeTokenConfig(message.feeToken); - uint32 tokenGasOverhead = s_onRamp.getTokenTransferFeeConfig(message.tokenAmounts[0].token).destGasOverhead; - uint32 destBytesOverhead = s_onRamp.getTokenTransferFeeConfig(message.tokenAmounts[0].token).destBytesOverhead; - uint32 tokenBytesOverhead = - destBytesOverhead == 0 ? uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) : destBytesOverhead; - - uint256 feeAmount = s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); - - uint256 gasUsed = GAS_LIMIT + DEST_GAS_OVERHEAD + tokenGasOverhead; - uint256 gasFeeUSD = (gasUsed * feeTokenConfig.gasMultiplierWeiPerEth * USD_PER_GAS); - (uint256 transferFeeUSD,,) = - s_onRamp.getTokenTransferCost(message.feeToken, feeTokenPrices[i], message.tokenAmounts); - uint256 messageFeeUSD = (transferFeeUSD * feeTokenConfig.premiumMultiplierWeiPerEth); - uint256 dataAvailabilityFeeUSD = s_onRamp.getDataAvailabilityCost( - USD_PER_DATA_AVAILABILITY_GAS, message.data.length, message.tokenAmounts.length, tokenBytesOverhead - ); - - uint256 totalPriceInFeeToken = (gasFeeUSD + messageFeeUSD + dataAvailabilityFeeUSD) / feeTokenPrices[i]; - assertEq(totalPriceInFeeToken, feeAmount); - } - } - - function test_MessageWithDataAndTokenTransfer_Success() public view { - address[2] memory testTokens = [s_sourceFeeToken, s_sourceRouter.getWrappedNative()]; - uint224[2] memory feeTokenPrices = [s_feeTokenPrice, s_wrappedTokenPrice]; - - uint256 customGasLimit = 1_000_000; - uint256 feeTokenAmount = 10000e18; - uint256 customTokenAmount = 200000e18; - for (uint256 i = 0; i < feeTokenPrices.length; ++i) { - Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ - receiver: abi.encode(OWNER), - data: "", - tokenAmounts: new Client.EVMTokenAmount[](2), - feeToken: testTokens[i], - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: customGasLimit})) - }); - EVM2EVMOnRamp.FeeTokenConfig memory feeTokenConfig = s_onRamp.getFeeTokenConfig(message.feeToken); - - message.tokenAmounts[0] = Client.EVMTokenAmount({token: s_sourceFeeToken, amount: feeTokenAmount}); - message.tokenAmounts[1] = Client.EVMTokenAmount({token: CUSTOM_TOKEN, amount: customTokenAmount}); - message.data = "random bits and bytes that should be factored into the cost of the message"; - - uint32 tokenGasOverhead = 0; - uint32 tokenBytesOverhead = 0; - for (uint256 j = 0; j < message.tokenAmounts.length; ++j) { - tokenGasOverhead += s_onRamp.getTokenTransferFeeConfig(message.tokenAmounts[j].token).destGasOverhead; - uint32 destBytesOverhead = s_onRamp.getTokenTransferFeeConfig(message.tokenAmounts[j].token).destBytesOverhead; - tokenBytesOverhead += destBytesOverhead == 0 ? uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) : destBytesOverhead; - } - - uint256 feeAmount = s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); - - uint256 gasUsed = - customGasLimit + DEST_GAS_OVERHEAD + message.data.length * DEST_GAS_PER_PAYLOAD_BYTE + tokenGasOverhead; - uint256 gasFeeUSD = (gasUsed * feeTokenConfig.gasMultiplierWeiPerEth * USD_PER_GAS); - (uint256 transferFeeUSD,,) = - s_onRamp.getTokenTransferCost(message.feeToken, feeTokenPrices[i], message.tokenAmounts); - uint256 messageFeeUSD = (transferFeeUSD * feeTokenConfig.premiumMultiplierWeiPerEth); - uint256 dataAvailabilityFeeUSD = s_onRamp.getDataAvailabilityCost( - USD_PER_DATA_AVAILABILITY_GAS, message.data.length, message.tokenAmounts.length, tokenBytesOverhead - ); - - uint256 totalPriceInFeeToken = (gasFeeUSD + messageFeeUSD + dataAvailabilityFeeUSD) / feeTokenPrices[i]; - assertEq(totalPriceInFeeToken, feeAmount); - } - } - - // Reverts - - function test_NotAFeeToken_Revert() public { - address notAFeeToken = address(0x111111); - Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(notAFeeToken, 1); - message.feeToken = notAFeeToken; - - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOnRamp.NotAFeeToken.selector, notAFeeToken)); - - s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); - } - - function test_MessageTooLarge_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.data = new bytes(MAX_DATA_SIZE + 1); - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOnRamp.MessageTooLarge.selector, MAX_DATA_SIZE, message.data.length)); - - s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); - } - - function test_TooManyTokens_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - uint256 tooMany = MAX_TOKENS_LENGTH + 1; - message.tokenAmounts = new Client.EVMTokenAmount[](tooMany); - vm.expectRevert(EVM2EVMOnRamp.UnsupportedNumberOfTokens.selector); - s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); - } - - // Asserts gasLimit must be <=maxGasLimit - function test_MessageGasLimitTooHigh_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: MAX_GAS_LIMIT + 1})); - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOnRamp.MessageGasLimitTooHigh.selector)); - s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); - } -} - -contract EVM2EVMOnRamp_setNops is EVM2EVMOnRampSetup { - // Used because EnumerableMap doesn't guarantee order - mapping(address nop => uint256 weight) internal s_nopsToWeights; - - function test_SetNops_Success() public { - EVM2EVMOnRamp.NopAndWeight[] memory nopsAndWeights = getNopsAndWeights(); - nopsAndWeights[1].nop = USER_4; - nopsAndWeights[1].weight = 20; - for (uint256 i = 0; i < nopsAndWeights.length; ++i) { - s_nopsToWeights[nopsAndWeights[i].nop] = nopsAndWeights[i].weight; - } - - s_onRamp.setNops(nopsAndWeights); - - (EVM2EVMOnRamp.NopAndWeight[] memory actual,) = s_onRamp.getNops(); - for (uint256 i = 0; i < actual.length; ++i) { - assertEq(actual[i].weight, s_nopsToWeights[actual[i].nop]); - } - } - - function test_AdminCanSetNops_Success() public { - EVM2EVMOnRamp.NopAndWeight[] memory nopsAndWeights = getNopsAndWeights(); - // Should not revert - vm.startPrank(ADMIN); - s_onRamp.setNops(nopsAndWeights); - } - - function test_IncludesPayment_Success() public { - EVM2EVMOnRamp.NopAndWeight[] memory nopsAndWeights = getNopsAndWeights(); - nopsAndWeights[1].nop = USER_4; - nopsAndWeights[1].weight = 20; - uint32 totalWeight; - for (uint256 i = 0; i < nopsAndWeights.length; ++i) { - totalWeight += nopsAndWeights[i].weight; - s_nopsToWeights[nopsAndWeights[i].nop] = nopsAndWeights[i].weight; - } - - // Make sure a payout happens regardless of what the weights are set to - uint96 nopFeesJuels = totalWeight * 5; - // Set Nop fee juels - deal(s_sourceFeeToken, address(s_onRamp), nopFeesJuels); - vm.startPrank(address(s_sourceRouter)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, _generateEmptyMessage(), nopFeesJuels, OWNER); - vm.startPrank(OWNER); - - // We don't care about the fee calculation logic in this test - // so we don't verify the amounts. We do verify the addresses to - // make sure the existing nops get paid and not the new ones. - EVM2EVMOnRamp.NopAndWeight[] memory existingNopsAndWeights = getNopsAndWeights(); - for (uint256 i = 0; i < existingNopsAndWeights.length; ++i) { - vm.expectEmit(true, false, false, false); - emit EVM2EVMOnRamp.NopPaid(existingNopsAndWeights[i].nop, 0); - } - - s_onRamp.setNops(nopsAndWeights); - - (EVM2EVMOnRamp.NopAndWeight[] memory actual,) = s_onRamp.getNops(); - for (uint256 i = 0; i < actual.length; ++i) { - assertEq(actual[i].weight, s_nopsToWeights[actual[i].nop]); - } - } - - function test_SetNopsRemovesOldNopsCompletely_Success() public { - EVM2EVMOnRamp.NopAndWeight[] memory nopsAndWeights = new EVM2EVMOnRamp.NopAndWeight[](0); - s_onRamp.setNops(nopsAndWeights); - (EVM2EVMOnRamp.NopAndWeight[] memory actual, uint256 totalWeight) = s_onRamp.getNops(); - assertEq(actual.length, 0); - assertEq(totalWeight, 0); - - address prevNop = getNopsAndWeights()[0].nop; - vm.startPrank(prevNop); - - // prev nop should not have permission to call payNops - vm.expectRevert(EVM2EVMOnRamp.OnlyCallableByOwnerOrAdminOrNop.selector); - s_onRamp.payNops(); - } - - // Reverts - - function test_NotEnoughFundsForPayout_Revert() public { - uint96 nopFeesJuels = MAX_NOP_FEES_JUELS; - // Set Nop fee juels but don't transfer LINK. This can happen when users - // pay in non-link tokens. - vm.startPrank(address(s_sourceRouter)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, _generateEmptyMessage(), nopFeesJuels, OWNER); - vm.startPrank(OWNER); - - vm.expectRevert(EVM2EVMOnRamp.InsufficientBalance.selector); - - s_onRamp.setNops(getNopsAndWeights()); - } - - function test_NonOwnerOrAdmin_Revert() public { - EVM2EVMOnRamp.NopAndWeight[] memory nopsAndWeights = getNopsAndWeights(); - vm.startPrank(STRANGER); - vm.expectRevert(EVM2EVMOnRamp.OnlyCallableByOwnerOrAdmin.selector); - s_onRamp.setNops(nopsAndWeights); - } - - function test_LinkTokenCannotBeNop_Revert() public { - EVM2EVMOnRamp.NopAndWeight[] memory nopsAndWeights = getNopsAndWeights(); - nopsAndWeights[0].nop = address(s_sourceTokens[0]); - - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOnRamp.InvalidNopAddress.selector, address(s_sourceTokens[0]))); - - s_onRamp.setNops(nopsAndWeights); - } - - function test_ZeroAddressCannotBeNop_Revert() public { - EVM2EVMOnRamp.NopAndWeight[] memory nopsAndWeights = getNopsAndWeights(); - nopsAndWeights[0].nop = address(0); - - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOnRamp.InvalidNopAddress.selector, address(0))); - - s_onRamp.setNops(nopsAndWeights); - } - - function test_TooManyNops_Revert() public { - EVM2EVMOnRamp.NopAndWeight[] memory nopsAndWeights = new EVM2EVMOnRamp.NopAndWeight[](257); - - vm.expectRevert(EVM2EVMOnRamp.TooManyNops.selector); - - s_onRamp.setNops(nopsAndWeights); - } -} - -contract EVM2EVMOnRamp_withdrawNonLinkFees is EVM2EVMOnRampSetup { - IERC20 internal s_token; - - function setUp() public virtual override { - EVM2EVMOnRampSetup.setUp(); - // Send some non-link tokens to the onRamp - s_token = IERC20(s_sourceTokens[1]); - deal(s_sourceTokens[1], address(s_onRamp), 100); - } - - function test_WithdrawNonLinkFees_Success() public { - s_onRamp.withdrawNonLinkFees(address(s_token), address(this)); - - assertEq(0, s_token.balanceOf(address(s_onRamp))); - assertEq(100, s_token.balanceOf(address(this))); - } - - function test_SettlingBalance_Success() public { - // Set Nop fee juels - uint96 nopFeesJuels = 10000000; - vm.startPrank(address(s_sourceRouter)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, _generateEmptyMessage(), nopFeesJuels, OWNER); - vm.startPrank(OWNER); - - vm.expectRevert(EVM2EVMOnRamp.LinkBalanceNotSettled.selector); - s_onRamp.withdrawNonLinkFees(address(s_token), address(this)); - - // It doesnt matter how the link tokens get to the onRamp - // In this case we simply deal them to the ramp to show - // anyone can settle the balance - deal(s_sourceTokens[0], address(s_onRamp), nopFeesJuels); - - s_onRamp.withdrawNonLinkFees(address(s_token), address(this)); - } - - function test_Fuzz_FuzzWithdrawalOnlyLeftoverLink_Success(uint96 nopFeeJuels, uint64 extraJuels) public { - nopFeeJuels = uint96(bound(nopFeeJuels, 1, MAX_NOP_FEES_JUELS)); - - // Set Nop fee juels - vm.startPrank(address(s_sourceRouter)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, _generateEmptyMessage(), nopFeeJuels, OWNER); - vm.startPrank(OWNER); - - vm.expectRevert(EVM2EVMOnRamp.LinkBalanceNotSettled.selector); - s_onRamp.withdrawNonLinkFees(address(s_token), address(this)); - - address linkToken = s_sourceTokens[0]; - // It doesnt matter how the link tokens get to the onRamp - // In this case we simply deal them to the ramp to show - // anyone can settle the balance - deal(linkToken, address(s_onRamp), nopFeeJuels + uint96(extraJuels)); - - // Now that we've sent nopFeesJuels + extraJuels, we should be able to withdraw extraJuels - address linkRecipient = address(0x123456789); - assertEq(0, IERC20(linkToken).balanceOf(linkRecipient)); - - s_onRamp.withdrawNonLinkFees(linkToken, linkRecipient); - - assertEq(extraJuels, IERC20(linkToken).balanceOf(linkRecipient)); - } - - // Reverts - - function test_LinkBalanceNotSettled_Revert() public { - // Set Nop fee juels - uint96 nopFeesJuels = 10000000; - vm.startPrank(address(s_sourceRouter)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, _generateEmptyMessage(), nopFeesJuels, OWNER); - vm.startPrank(OWNER); - - vm.expectRevert(EVM2EVMOnRamp.LinkBalanceNotSettled.selector); - - s_onRamp.withdrawNonLinkFees(address(s_token), address(this)); - } - - function test_NonOwnerOrAdmin_Revert() public { - vm.startPrank(STRANGER); - - vm.expectRevert(EVM2EVMOnRamp.OnlyCallableByOwnerOrAdmin.selector); - s_onRamp.withdrawNonLinkFees(address(s_token), address(this)); - } - - function test_WithdrawToZeroAddress_Revert() public { - vm.expectRevert(EVM2EVMOnRamp.InvalidWithdrawParams.selector); - s_onRamp.withdrawNonLinkFees(address(s_token), address(0)); - } -} - -contract EVM2EVMOnRamp_setFeeTokenConfig is EVM2EVMOnRampSetup { - function test_SetFeeTokenConfig_Success() public { - EVM2EVMOnRamp.FeeTokenConfigArgs[] memory feeConfig; - - vm.expectEmit(); - emit EVM2EVMOnRamp.FeeConfigSet(feeConfig); - - s_onRamp.setFeeTokenConfig(feeConfig); - } - - function test_SetFeeTokenConfigByAdmin_Success() public { - EVM2EVMOnRamp.FeeTokenConfigArgs[] memory feeConfig; - - vm.startPrank(ADMIN); - - vm.expectEmit(); - emit EVM2EVMOnRamp.FeeConfigSet(feeConfig); - - s_onRamp.setFeeTokenConfig(feeConfig); - } - - // Reverts - - function test_OnlyCallableByOwnerOrAdmin_Revert() public { - EVM2EVMOnRamp.FeeTokenConfigArgs[] memory feeConfig; - vm.startPrank(STRANGER); - - vm.expectRevert(EVM2EVMOnRamp.OnlyCallableByOwnerOrAdmin.selector); - - s_onRamp.setFeeTokenConfig(feeConfig); - } -} - -contract EVM2EVMOnRamp_setTokenTransferFeeConfig is EVM2EVMOnRampSetup { - function test__setTokenTransferFeeConfig_Success() public { - EVM2EVMOnRamp.TokenTransferFeeConfigArgs[] memory tokenTransferFeeArgs = - new EVM2EVMOnRamp.TokenTransferFeeConfigArgs[](2); - tokenTransferFeeArgs[0] = EVM2EVMOnRamp.TokenTransferFeeConfigArgs({ - token: address(5), - minFeeUSDCents: 6, - maxFeeUSDCents: 7, - deciBps: 8, - destGasOverhead: 9, - destBytesOverhead: 312, - aggregateRateLimitEnabled: true - }); - tokenTransferFeeArgs[1] = EVM2EVMOnRamp.TokenTransferFeeConfigArgs({ - token: address(11), - minFeeUSDCents: 12, - maxFeeUSDCents: 13, - deciBps: 14, - destGasOverhead: 15, - destBytesOverhead: 394, - aggregateRateLimitEnabled: false - }); - - vm.expectEmit(); - emit EVM2EVMOnRamp.TokenTransferFeeConfigSet(tokenTransferFeeArgs); - - s_onRamp.setTokenTransferFeeConfig(tokenTransferFeeArgs, new address[](0)); - - EVM2EVMOnRamp.TokenTransferFeeConfig memory config0 = - s_onRamp.getTokenTransferFeeConfig(tokenTransferFeeArgs[0].token); - - assertEq(tokenTransferFeeArgs[0].minFeeUSDCents, config0.minFeeUSDCents); - assertEq(tokenTransferFeeArgs[0].maxFeeUSDCents, config0.maxFeeUSDCents); - assertEq(tokenTransferFeeArgs[0].deciBps, config0.deciBps); - assertEq(tokenTransferFeeArgs[0].destGasOverhead, config0.destGasOverhead); - assertEq(tokenTransferFeeArgs[0].destBytesOverhead, config0.destBytesOverhead); - assertEq(tokenTransferFeeArgs[0].aggregateRateLimitEnabled, config0.aggregateRateLimitEnabled); - assertTrue(config0.isEnabled); - - EVM2EVMOnRamp.TokenTransferFeeConfig memory config1 = - s_onRamp.getTokenTransferFeeConfig(tokenTransferFeeArgs[1].token); - - assertEq(tokenTransferFeeArgs[1].minFeeUSDCents, config1.minFeeUSDCents); - assertEq(tokenTransferFeeArgs[1].maxFeeUSDCents, config1.maxFeeUSDCents); - assertEq(tokenTransferFeeArgs[1].deciBps, config1.deciBps); - assertEq(tokenTransferFeeArgs[1].destGasOverhead, config1.destGasOverhead); - assertEq(tokenTransferFeeArgs[1].destBytesOverhead, config1.destBytesOverhead); - assertEq(tokenTransferFeeArgs[1].aggregateRateLimitEnabled, config1.aggregateRateLimitEnabled); - assertTrue(config0.isEnabled); - - // Remove only the first token and validate only the first token is removed - address[] memory tokensToRemove = new address[](1); - tokensToRemove[0] = tokenTransferFeeArgs[0].token; - - vm.expectEmit(); - emit EVM2EVMOnRamp.TokenTransferFeeConfigDeleted(tokensToRemove); - - s_onRamp.setTokenTransferFeeConfig(new EVM2EVMOnRamp.TokenTransferFeeConfigArgs[](0), tokensToRemove); - - config0 = s_onRamp.getTokenTransferFeeConfig(tokenTransferFeeArgs[0].token); - - assertEq(0, config0.minFeeUSDCents); - assertEq(0, config0.maxFeeUSDCents); - assertEq(0, config0.deciBps); - assertEq(0, config0.destGasOverhead); - assertEq(0, config0.destBytesOverhead); - assertFalse(config0.aggregateRateLimitEnabled); - assertFalse(config0.isEnabled); - - config1 = s_onRamp.getTokenTransferFeeConfig(tokenTransferFeeArgs[1].token); - - assertEq(tokenTransferFeeArgs[1].minFeeUSDCents, config1.minFeeUSDCents); - assertEq(tokenTransferFeeArgs[1].maxFeeUSDCents, config1.maxFeeUSDCents); - assertEq(tokenTransferFeeArgs[1].deciBps, config1.deciBps); - assertEq(tokenTransferFeeArgs[1].destGasOverhead, config1.destGasOverhead); - assertEq(tokenTransferFeeArgs[1].destBytesOverhead, config1.destBytesOverhead); - assertEq(tokenTransferFeeArgs[1].aggregateRateLimitEnabled, config1.aggregateRateLimitEnabled); - assertTrue(config1.isEnabled); - } - - function test__setTokenTransferFeeConfig_byAdmin_Success() public { - EVM2EVMOnRamp.TokenTransferFeeConfigArgs[] memory transferFeeConfig; - vm.startPrank(ADMIN); - - vm.expectEmit(); - emit EVM2EVMOnRamp.TokenTransferFeeConfigSet(transferFeeConfig); - - s_onRamp.setTokenTransferFeeConfig(transferFeeConfig, new address[](0)); - } - - // Reverts - - function test__setTokenTransferFeeConfig_InvalidDestBytesOverhead_Revert() public { - EVM2EVMOnRamp.TokenTransferFeeConfigArgs[] memory transferFeeConfig = - new EVM2EVMOnRamp.TokenTransferFeeConfigArgs[](1); - transferFeeConfig[0].destBytesOverhead = uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) - 1; - vm.expectRevert( - abi.encodeWithSelector( - EVM2EVMOnRamp.InvalidDestBytesOverhead.selector, - transferFeeConfig[0].token, - transferFeeConfig[0].destBytesOverhead - ) - ); - s_onRamp.setTokenTransferFeeConfig(transferFeeConfig, new address[](0)); - } - - function test__setTokenTransferFeeConfig_OnlyCallableByOwnerOrAdmin_Revert() public { - EVM2EVMOnRamp.TokenTransferFeeConfigArgs[] memory transferFeeConfig; - vm.startPrank(STRANGER); - - vm.expectRevert(EVM2EVMOnRamp.OnlyCallableByOwnerOrAdmin.selector); - - s_onRamp.setTokenTransferFeeConfig(transferFeeConfig, new address[](0)); - } -} - -contract EVM2EVMOnRamp_getTokenPool is EVM2EVMOnRampSetup { - function test_GetTokenPool_Success() public view { - assertEq( - s_sourcePoolByToken[s_sourceTokens[0]], - address(s_onRamp.getPoolBySourceToken(DEST_CHAIN_SELECTOR, IERC20(s_sourceTokens[0]))) - ); - assertEq( - s_sourcePoolByToken[s_sourceTokens[1]], - address(s_onRamp.getPoolBySourceToken(DEST_CHAIN_SELECTOR, IERC20(s_sourceTokens[1]))) - ); - - address wrongToken = address(123); - address nonExistentPool = address(s_onRamp.getPoolBySourceToken(DEST_CHAIN_SELECTOR, IERC20(wrongToken))); - - assertEq(address(0), nonExistentPool); - } -} - -contract EVM2EVMOnRamp_setDynamicConfig is EVM2EVMOnRampSetup { - function test_SetDynamicConfig_Success() public { - EVM2EVMOnRamp.StaticConfig memory staticConfig = s_onRamp.getStaticConfig(); - EVM2EVMOnRamp.DynamicConfig memory newConfig = EVM2EVMOnRamp.DynamicConfig({ - router: address(2134), - maxNumberOfTokensPerMsg: 14, - destGasOverhead: DEST_GAS_OVERHEAD / 2, - destGasPerPayloadByte: DEST_GAS_PER_PAYLOAD_BYTE / 2, - destDataAvailabilityOverheadGas: DEST_DATA_AVAILABILITY_OVERHEAD_GAS, - destGasPerDataAvailabilityByte: DEST_GAS_PER_DATA_AVAILABILITY_BYTE, - destDataAvailabilityMultiplierBps: DEST_GAS_DATA_AVAILABILITY_MULTIPLIER_BPS, - priceRegistry: address(23423), - maxDataBytes: 400, - maxPerMsgGasLimit: MAX_GAS_LIMIT / 2, - defaultTokenFeeUSDCents: DEFAULT_TOKEN_FEE_USD_CENTS, - defaultTokenDestGasOverhead: DEFAULT_TOKEN_DEST_GAS_OVERHEAD, - enforceOutOfOrder: false - }); - - vm.expectEmit(); - emit EVM2EVMOnRamp.ConfigSet(staticConfig, newConfig); - - s_onRamp.setDynamicConfig(newConfig); - - EVM2EVMOnRamp.DynamicConfig memory gotDynamicConfig = s_onRamp.getDynamicConfig(); - assertEq(newConfig.router, gotDynamicConfig.router); - assertEq(newConfig.maxNumberOfTokensPerMsg, gotDynamicConfig.maxNumberOfTokensPerMsg); - assertEq(newConfig.destGasOverhead, gotDynamicConfig.destGasOverhead); - assertEq(newConfig.destGasPerPayloadByte, gotDynamicConfig.destGasPerPayloadByte); - assertEq(newConfig.priceRegistry, gotDynamicConfig.priceRegistry); - assertEq(newConfig.maxDataBytes, gotDynamicConfig.maxDataBytes); - assertEq(newConfig.maxPerMsgGasLimit, gotDynamicConfig.maxPerMsgGasLimit); - } - - // Reverts - - function test_SetConfigInvalidConfig_Revert() public { - EVM2EVMOnRamp.DynamicConfig memory newConfig = EVM2EVMOnRamp.DynamicConfig({ - router: address(1), - maxNumberOfTokensPerMsg: 14, - destGasOverhead: DEST_GAS_OVERHEAD / 2, - destGasPerPayloadByte: DEST_GAS_PER_PAYLOAD_BYTE / 2, - destDataAvailabilityOverheadGas: DEST_DATA_AVAILABILITY_OVERHEAD_GAS, - destGasPerDataAvailabilityByte: DEST_GAS_PER_DATA_AVAILABILITY_BYTE, - destDataAvailabilityMultiplierBps: DEST_GAS_DATA_AVAILABILITY_MULTIPLIER_BPS, - priceRegistry: address(23423), - maxDataBytes: 400, - maxPerMsgGasLimit: MAX_GAS_LIMIT / 2, - defaultTokenFeeUSDCents: DEFAULT_TOKEN_FEE_USD_CENTS, - defaultTokenDestGasOverhead: DEFAULT_TOKEN_DEST_GAS_OVERHEAD, - enforceOutOfOrder: false - }); - - // Invalid price reg reverts. - newConfig.priceRegistry = address(0); - vm.expectRevert(EVM2EVMOnRamp.InvalidConfig.selector); - s_onRamp.setDynamicConfig(newConfig); - - // Succeeds if valid - newConfig.priceRegistry = address(23423); - s_onRamp.setDynamicConfig(newConfig); - } - - function test_SetConfigOnlyOwner_Revert() public { - vm.startPrank(STRANGER); - vm.expectRevert("Only callable by owner"); - s_onRamp.setDynamicConfig(generateDynamicOnRampConfig(address(1), address(2))); - vm.startPrank(ADMIN); - vm.expectRevert("Only callable by owner"); - s_onRamp.setDynamicConfig(generateDynamicOnRampConfig(address(1), address(2))); - } -} diff --git a/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMOnRampSetup.t.sol b/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMOnRampSetup.t.sol deleted file mode 100644 index 84350448a13..00000000000 --- a/contracts/src/v0.8/ccip/test/onRamp/EVM2EVMOnRampSetup.t.sol +++ /dev/null @@ -1,251 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {Router} from "../../Router.sol"; -import {Client} from "../../libraries/Client.sol"; -import {Internal} from "../../libraries/Internal.sol"; -import {Pool} from "../../libraries/Pool.sol"; -import {EVM2EVMOnRamp} from "../../onRamp/EVM2EVMOnRamp.sol"; -import {TokenSetup} from "../TokenSetup.t.sol"; - -import {FeeQuoterSetup} from "../feeQuoter/FeeQuoterSetup.t.sol"; -import {EVM2EVMOnRampHelper} from "../helpers/EVM2EVMOnRampHelper.sol"; - -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; - -contract EVM2EVMOnRampSetup is TokenSetup, FeeQuoterSetup { - uint256 internal immutable i_tokenAmount0 = 9; - uint256 internal immutable i_tokenAmount1 = 7; - - bytes32 internal s_metadataHash; - - EVM2EVMOnRampHelper internal s_onRamp; - address[] internal s_offRamps; - - address internal s_destTokenPool = makeAddr("destTokenPool"); - address internal s_destToken = makeAddr("destToken"); - - EVM2EVMOnRamp.FeeTokenConfigArgs[] internal s_feeTokenConfigArgs; - EVM2EVMOnRamp.TokenTransferFeeConfigArgs[] internal s_tokenTransferFeeConfigArgs; - - function setUp() public virtual override(TokenSetup, FeeQuoterSetup) { - TokenSetup.setUp(); - FeeQuoterSetup.setUp(); - - s_feeQuoter.updatePrices(_getSingleTokenPriceUpdateStruct(CUSTOM_TOKEN, CUSTOM_TOKEN_PRICE)); - - address WETH = s_sourceRouter.getWrappedNative(); - - s_feeTokenConfigArgs.push( - EVM2EVMOnRamp.FeeTokenConfigArgs({ - token: s_sourceFeeToken, - networkFeeUSDCents: 1_00, // 1 USD - gasMultiplierWeiPerEth: 1e18, // 1x - premiumMultiplierWeiPerEth: 5e17, // 0.5x - enabled: true - }) - ); - s_feeTokenConfigArgs.push( - EVM2EVMOnRamp.FeeTokenConfigArgs({ - token: WETH, - networkFeeUSDCents: 5_00, // 5 USD - gasMultiplierWeiPerEth: 2e18, // 2x - premiumMultiplierWeiPerEth: 2e18, // 2x - enabled: true - }) - ); - - s_tokenTransferFeeConfigArgs.push( - EVM2EVMOnRamp.TokenTransferFeeConfigArgs({ - token: s_sourceFeeToken, - minFeeUSDCents: 1_00, // 1 USD - maxFeeUSDCents: 1000_00, // 1,000 USD - deciBps: 2_5, // 2.5 bps, or 0.025% - destGasOverhead: 84_000, - destBytesOverhead: uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES), - aggregateRateLimitEnabled: true - }) - ); - s_tokenTransferFeeConfigArgs.push( - EVM2EVMOnRamp.TokenTransferFeeConfigArgs({ - token: CUSTOM_TOKEN, - minFeeUSDCents: 2_00, // 1 USD - maxFeeUSDCents: 500_00, // 500 USD - deciBps: 10_0, // 10 bps, or 0.1% - destGasOverhead: 83_000, - destBytesOverhead: 200, - aggregateRateLimitEnabled: true - }) - ); - - s_onRamp = new EVM2EVMOnRampHelper( - EVM2EVMOnRamp.StaticConfig({ - linkToken: s_sourceTokens[0], - chainSelector: SOURCE_CHAIN_SELECTOR, - destChainSelector: DEST_CHAIN_SELECTOR, - defaultTxGasLimit: GAS_LIMIT, - maxNopFeesJuels: MAX_NOP_FEES_JUELS, - prevOnRamp: address(0), - rmnProxy: address(s_mockRMN), - tokenAdminRegistry: address(s_tokenAdminRegistry) - }), - generateDynamicOnRampConfig(address(s_sourceRouter), address(s_feeQuoter)), - _getOutboundRateLimiterConfig(), - s_feeTokenConfigArgs, - s_tokenTransferFeeConfigArgs, - getNopsAndWeights() - ); - s_onRamp.setAdmin(ADMIN); - - s_metadataHash = keccak256( - abi.encode(Internal.EVM_2_EVM_MESSAGE_HASH, SOURCE_CHAIN_SELECTOR, DEST_CHAIN_SELECTOR, address(s_onRamp)) - ); - - s_offRamps = new address[](2); - s_offRamps[0] = address(10); - s_offRamps[1] = address(11); - Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); - Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](2); - onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: address(s_onRamp)}); - offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: s_offRamps[0]}); - offRampUpdates[1] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: s_offRamps[1]}); - s_sourceRouter.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); - - // Pre approve the first token so the gas estimates of the tests - // only cover actual gas usage from the ramps - IERC20(s_sourceTokens[0]).approve(address(s_sourceRouter), 2 ** 128); - IERC20(s_sourceTokens[1]).approve(address(s_sourceRouter), 2 ** 128); - } - - function getNopsAndWeights() internal pure returns (EVM2EVMOnRamp.NopAndWeight[] memory) { - EVM2EVMOnRamp.NopAndWeight[] memory nopsAndWeights = new EVM2EVMOnRamp.NopAndWeight[](3); - nopsAndWeights[0] = EVM2EVMOnRamp.NopAndWeight({nop: USER_1, weight: 19284}); - nopsAndWeights[1] = EVM2EVMOnRamp.NopAndWeight({nop: USER_2, weight: 52935}); - nopsAndWeights[2] = EVM2EVMOnRamp.NopAndWeight({nop: USER_3, weight: 8}); - return nopsAndWeights; - } - - function generateDynamicOnRampConfig( - address router, - address priceRegistry - ) internal pure returns (EVM2EVMOnRamp.DynamicConfig memory) { - return EVM2EVMOnRamp.DynamicConfig({ - router: router, - maxNumberOfTokensPerMsg: MAX_TOKENS_LENGTH, - destGasOverhead: DEST_GAS_OVERHEAD, - destGasPerPayloadByte: DEST_GAS_PER_PAYLOAD_BYTE, - destDataAvailabilityOverheadGas: DEST_DATA_AVAILABILITY_OVERHEAD_GAS, - destGasPerDataAvailabilityByte: DEST_GAS_PER_DATA_AVAILABILITY_BYTE, - destDataAvailabilityMultiplierBps: DEST_GAS_DATA_AVAILABILITY_MULTIPLIER_BPS, - priceRegistry: priceRegistry, - maxDataBytes: MAX_DATA_SIZE, - maxPerMsgGasLimit: MAX_GAS_LIMIT, - defaultTokenFeeUSDCents: DEFAULT_TOKEN_FEE_USD_CENTS, - defaultTokenDestGasOverhead: DEFAULT_TOKEN_DEST_GAS_OVERHEAD, - enforceOutOfOrder: false - }); - } - - function _generateTokenMessage() public view returns (Client.EVM2AnyMessage memory) { - Client.EVMTokenAmount[] memory tokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); - tokenAmounts[0].amount = i_tokenAmount0; - tokenAmounts[1].amount = i_tokenAmount1; - return Client.EVM2AnyMessage({ - receiver: abi.encode(OWNER), - data: "", - tokenAmounts: tokenAmounts, - feeToken: s_sourceFeeToken, - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT})) - }); - } - - function _generateSingleTokenMessage( - address token, - uint256 amount - ) public view returns (Client.EVM2AnyMessage memory) { - Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); - tokenAmounts[0] = Client.EVMTokenAmount({token: token, amount: amount}); - - return Client.EVM2AnyMessage({ - receiver: abi.encode(OWNER), - data: "", - tokenAmounts: tokenAmounts, - feeToken: s_sourceFeeToken, - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT})) - }); - } - - function _generateEmptyMessage() public view returns (Client.EVM2AnyMessage memory) { - return Client.EVM2AnyMessage({ - receiver: abi.encode(OWNER), - data: "", - tokenAmounts: new Client.EVMTokenAmount[](0), - feeToken: s_sourceFeeToken, - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT})) - }); - } - - function _messageToEvent( - Client.EVM2AnyMessage memory message, - uint64 seqNum, - uint64 nonce, - uint256 feeTokenAmount, - address originalSender - ) public view returns (Internal.EVM2EVMMessage memory) { - // Slicing is only available for calldata. So we have to build a new bytes array. - bytes memory args = new bytes(message.extraArgs.length - 4); - for (uint256 i = 4; i < message.extraArgs.length; ++i) { - args[i - 4] = message.extraArgs[i]; - } - uint256 numberOfTokens = message.tokenAmounts.length; - Client.EVMExtraArgsV2 memory extraArgs = _extraArgsFromBytes(bytes4(message.extraArgs), args); - Internal.EVM2EVMMessage memory messageEvent = Internal.EVM2EVMMessage({ - sequenceNumber: seqNum, - feeTokenAmount: feeTokenAmount, - sender: originalSender, - nonce: extraArgs.allowOutOfOrderExecution ? 0 : nonce, - gasLimit: extraArgs.gasLimit, - strict: false, - sourceChainSelector: SOURCE_CHAIN_SELECTOR, - receiver: abi.decode(message.receiver, (address)), - data: message.data, - tokenAmounts: message.tokenAmounts, - sourceTokenData: new bytes[](numberOfTokens), - feeToken: message.feeToken, - messageId: "" - }); - - for (uint256 i = 0; i < numberOfTokens; ++i) { - EVM2EVMOnRamp.TokenTransferFeeConfig memory tokenTransferFeeConfig = - s_onRamp.getTokenTransferFeeConfig(message.tokenAmounts[i].token); - - messageEvent.sourceTokenData[i] = abi.encode( - Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[message.tokenAmounts[i].token]), - destTokenAddress: abi.encode(s_destTokenBySourceToken[message.tokenAmounts[i].token]), - extraData: "", - destGasAmount: tokenTransferFeeConfig.isEnabled - ? tokenTransferFeeConfig.destGasOverhead - : DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }) - ); - } - - messageEvent.messageId = Internal._hash(messageEvent, s_metadataHash); - return messageEvent; - } - - function _extraArgsFromBytes( - bytes4 sig, - bytes memory extraArgData - ) public pure returns (Client.EVMExtraArgsV2 memory) { - if (sig == Client.EVM_EXTRA_ARGS_V1_TAG) { - Client.EVMExtraArgsV1 memory extraArgsV1 = abi.decode(extraArgData, (Client.EVMExtraArgsV1)); - return Client.EVMExtraArgsV2({gasLimit: extraArgsV1.gasLimit, allowOutOfOrderExecution: false}); - } else if (sig == Client.EVM_EXTRA_ARGS_V2_TAG) { - return abi.decode(extraArgData, (Client.EVMExtraArgsV2)); - } else { - revert("Invalid extraArgs tag"); - } - } -} diff --git a/contracts/src/v0.8/ccip/test/onRamp/OnRamp.t.sol b/contracts/src/v0.8/ccip/test/onRamp/OnRamp.t.sol index 16889db39eb..e771ebd3123 100644 --- a/contracts/src/v0.8/ccip/test/onRamp/OnRamp.t.sol +++ b/contracts/src/v0.8/ccip/test/onRamp/OnRamp.t.sol @@ -406,7 +406,7 @@ contract OnRamp_forwardFromRouter is OnRampSetup { message.tokenAmounts[0].amount = 1e18; message.tokenAmounts[0].token = s_sourceTokens[0]; IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - s_outboundmessageInterceptor.setMessageIdValidationState(keccak256(abi.encode(message)), false); + s_outboundMessageInterceptor.setMessageIdValidationState(keccak256(abi.encode(message)), false); vm.expectEmit(); emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, feeAmount, OWNER)); @@ -463,7 +463,7 @@ contract OnRamp_forwardFromRouter is OnRampSetup { message.tokenAmounts[0].amount = 1e18; message.tokenAmounts[0].token = s_sourceTokens[0]; IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - s_outboundmessageInterceptor.setMessageIdValidationState(keccak256(abi.encode(message)), true); + s_outboundMessageInterceptor.setMessageIdValidationState(keccak256(abi.encode(message)), true); vm.expectRevert( abi.encodeWithSelector(IMessageInterceptor.MessageValidationError.selector, bytes("Invalid message")) diff --git a/contracts/src/v0.8/ccip/test/onRamp/OnRampSetup.t.sol b/contracts/src/v0.8/ccip/test/onRamp/OnRampSetup.t.sol index 541146bdaf5..8e09b44c0b4 100644 --- a/contracts/src/v0.8/ccip/test/onRamp/OnRampSetup.t.sol +++ b/contracts/src/v0.8/ccip/test/onRamp/OnRampSetup.t.sol @@ -22,14 +22,14 @@ contract OnRampSetup is FeeQuoterFeeSetup { bytes32 internal s_metadataHash; OnRampHelper internal s_onRamp; - MessageInterceptorHelper internal s_outboundmessageInterceptor; + MessageInterceptorHelper internal s_outboundMessageInterceptor; address[] internal s_offRamps; NonceManager internal s_outboundNonceManager; function setUp() public virtual override { super.setUp(); - s_outboundmessageInterceptor = new MessageInterceptorHelper(); + s_outboundMessageInterceptor = new MessageInterceptorHelper(); s_outboundNonceManager = new NonceManager(new address[](0)); (s_onRamp, s_metadataHash) = _deployOnRamp( SOURCE_CHAIN_SELECTOR, s_sourceRouter, address(s_outboundNonceManager), address(s_tokenAdminRegistry) @@ -65,7 +65,7 @@ contract OnRampSetup is FeeQuoterFeeSetup { } /// @dev a helper function to compose EVM2AnyRampMessage messages - /// @dev it is assummed that LINK is the payment token because feeTokenAmount == feeValueJuels + /// @dev it is assumed that LINK is the payment token because feeTokenAmount == feeValueJuels function _messageToEvent( Client.EVM2AnyMessage memory message, uint64 seqNum, @@ -78,7 +78,7 @@ contract OnRampSetup is FeeQuoterFeeSetup { seqNum, nonce, feeTokenAmount, // fee paid - feeTokenAmount, // converstion to jules is the same + feeTokenAmount, // conversion to jules is the same originalSender ); } @@ -173,7 +173,7 @@ contract OnRampSetup is FeeQuoterFeeSetup { } OnRamp.DynamicConfig memory dynamicConfig = s_onRamp.getDynamicConfig(); - dynamicConfig.messageInterceptor = address(s_outboundmessageInterceptor); + dynamicConfig.messageInterceptor = address(s_outboundMessageInterceptor); s_onRamp.setDynamicConfig(dynamicConfig); if (resetPrank) { diff --git a/contracts/src/v0.8/ccip/test/pools/BurnWithFromMintRebasingTokenPool.t.sol b/contracts/src/v0.8/ccip/test/pools/BurnWithFromMintRebasingTokenPool.t.sol deleted file mode 100644 index c88f030c563..00000000000 --- a/contracts/src/v0.8/ccip/test/pools/BurnWithFromMintRebasingTokenPool.t.sol +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {IBurnMintERC20} from "../../../shared/token/ERC20/IBurnMintERC20.sol"; - -import {Pool} from "../../libraries/Pool.sol"; -import {BurnWithFromMintRebasingTokenPool} from "../../pools/BurnWithFromMintRebasingTokenPool.sol"; -import {BurnMintSetup} from "./BurnMintSetup.t.sol"; - -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {ERC20RebasingHelper} from "../helpers/ERC20RebasingHelper.sol"; - -contract BurnWithFromMintRebasingTokenPoolSetup is BurnMintSetup { - BurnWithFromMintRebasingTokenPool internal s_pool; - ERC20RebasingHelper internal s_rebasingToken; - - function setUp() public virtual override { - BurnMintSetup.setUp(); - - s_rebasingToken = new ERC20RebasingHelper(); - - s_pool = new BurnWithFromMintRebasingTokenPool( - IBurnMintERC20(address(s_rebasingToken)), new address[](0), address(s_mockRMN), address(s_sourceRouter) - ); - - _applyChainUpdates(address(s_pool)); - - deal(address(s_rebasingToken), OWNER, 1e18); - - vm.startPrank(s_burnMintOffRamp); - } -} - -contract BurnWithFromMintTokenPool_releaseOrMint is BurnWithFromMintRebasingTokenPoolSetup { - function test_Setup_Success() public view { - assertEq(address(s_rebasingToken), address(s_pool.getToken())); - assertEq(address(s_mockRMN), s_pool.getRmnProxy()); - assertEq(false, s_pool.getAllowListEnabled()); - assertEq(type(uint256).max, s_rebasingToken.allowance(address(s_pool), address(s_pool))); - assertEq("BurnWithFromMintRebasingTokenPool 1.5.0", s_pool.typeAndVersion()); - } - - function test_releaseOrMint_Success() public { - uint256 amount = 1000; - uint256 balancePre = s_rebasingToken.balanceOf(address(OWNER)); - - Pool.ReleaseOrMintOutV1 memory releaseOrMintOut = s_pool.releaseOrMint(_getReleaseOrMintIn(amount)); - - assertEq(amount, releaseOrMintOut.destinationAmount); - assertEq(balancePre + amount, s_rebasingToken.balanceOf(address(OWNER))); - } - - function testFuzz_releaseOrMint_rebasing_success(uint16 multiplierPercentage) public { - uint256 amount = 1000; - uint256 expectedAmount = amount * multiplierPercentage / 100; - s_rebasingToken.setMultiplierPercentage(multiplierPercentage); - - uint256 balancePre = s_rebasingToken.balanceOf(address(OWNER)); - - Pool.ReleaseOrMintOutV1 memory releaseOrMintOut = s_pool.releaseOrMint(_getReleaseOrMintIn(amount)); - - assertEq(expectedAmount, releaseOrMintOut.destinationAmount); - assertEq(balancePre + expectedAmount, s_rebasingToken.balanceOf(address(OWNER))); - } - - function test_releaseOrMint_NegativeMintAmount_reverts() public { - uint256 amount = 1000; - s_rebasingToken.setMintShouldBurn(true); - - vm.expectRevert(abi.encodeWithSelector(BurnWithFromMintRebasingTokenPool.NegativeMintAmount.selector, amount)); - - s_pool.releaseOrMint(_getReleaseOrMintIn(amount)); - } - - function _getReleaseOrMintIn(uint256 amount) internal view returns (Pool.ReleaseOrMintInV1 memory) { - return Pool.ReleaseOrMintInV1({ - originalSender: bytes(""), - receiver: OWNER, - amount: amount, - localToken: address(s_rebasingToken), - remoteChainSelector: DEST_CHAIN_SELECTOR, - sourcePoolAddress: abi.encode(s_remoteBurnMintPool), - sourcePoolData: "", - offchainTokenData: "" - }); - } -} diff --git a/contracts/src/v0.8/ccip/test/rateLimiter/AggregateRateLimiter.t.sol b/contracts/src/v0.8/ccip/test/rateLimiter/AggregateRateLimiter.t.sol deleted file mode 100644 index 90b56d4370a..00000000000 --- a/contracts/src/v0.8/ccip/test/rateLimiter/AggregateRateLimiter.t.sol +++ /dev/null @@ -1,237 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {IPriceRegistry} from "../../interfaces/IPriceRegistry.sol"; - -import {AggregateRateLimiter} from "../../AggregateRateLimiter.sol"; -import {Client} from "../../libraries/Client.sol"; -import {Internal} from "../../libraries/Internal.sol"; -import {RateLimiter} from "../../libraries/RateLimiter.sol"; - -import {FeeQuoterSetup} from "../feeQuoter/FeeQuoterSetup.t.sol"; -import {AggregateRateLimiterHelper} from "../helpers/AggregateRateLimiterHelper.sol"; - -import {stdError} from "forge-std/Test.sol"; - -contract AggregateTokenLimiterSetup is FeeQuoterSetup { - AggregateRateLimiterHelper internal s_rateLimiter; - RateLimiter.Config internal s_config; - - address internal immutable TOKEN = 0x21118E64E1fB0c487F25Dd6d3601FF6af8D32E4e; - uint224 internal constant TOKEN_PRICE = 4e18; - - function setUp() public virtual override { - FeeQuoterSetup.setUp(); - - Internal.PriceUpdates memory priceUpdates = _getSingleTokenPriceUpdateStruct(TOKEN, TOKEN_PRICE); - s_feeQuoter.updatePrices(priceUpdates); - - s_config = RateLimiter.Config({isEnabled: true, rate: 5, capacity: 100}); - s_rateLimiter = new AggregateRateLimiterHelper(s_config); - s_rateLimiter.setAdmin(ADMIN); - } -} - -contract AggregateTokenLimiter_constructor is AggregateTokenLimiterSetup { - function test_Constructor_Success() public view { - assertEq(ADMIN, s_rateLimiter.getTokenLimitAdmin()); - assertEq(OWNER, s_rateLimiter.owner()); - - RateLimiter.TokenBucket memory bucket = s_rateLimiter.currentRateLimiterState(); - assertEq(s_config.rate, bucket.rate); - assertEq(s_config.capacity, bucket.capacity); - assertEq(s_config.capacity, bucket.tokens); - assertEq(s_config.isEnabled, bucket.isEnabled); - assertEq(BLOCK_TIME, bucket.lastUpdated); - } -} - -contract AggregateTokenLimiter_getTokenLimitAdmin is AggregateTokenLimiterSetup { - function test_GetTokenLimitAdmin_Success() public view { - assertEq(ADMIN, s_rateLimiter.getTokenLimitAdmin()); - } -} - -contract AggregateTokenLimiter_setAdmin is AggregateTokenLimiterSetup { - function test_Owner_Success() public { - vm.expectEmit(); - emit AggregateRateLimiter.AdminSet(STRANGER); - - s_rateLimiter.setAdmin(STRANGER); - assertEq(STRANGER, s_rateLimiter.getTokenLimitAdmin()); - } - - // Reverts - - function test_OnlyOwnerOrAdmin_Revert() public { - vm.startPrank(STRANGER); - vm.expectRevert(RateLimiter.OnlyCallableByAdminOrOwner.selector); - - s_rateLimiter.setAdmin(STRANGER); - } -} - -contract AggregateTokenLimiter_getTokenBucket is AggregateTokenLimiterSetup { - function test_GetTokenBucket_Success() public view { - RateLimiter.TokenBucket memory bucket = s_rateLimiter.currentRateLimiterState(); - assertEq(s_config.rate, bucket.rate); - assertEq(s_config.capacity, bucket.capacity); - assertEq(s_config.capacity, bucket.tokens); - assertEq(BLOCK_TIME, bucket.lastUpdated); - } - - function test_Refill_Success() public { - s_config.capacity = s_config.capacity * 2; - s_rateLimiter.setRateLimiterConfig(s_config); - - RateLimiter.TokenBucket memory bucket = s_rateLimiter.currentRateLimiterState(); - - assertEq(s_config.rate, bucket.rate); - assertEq(s_config.capacity, bucket.capacity); - assertEq(s_config.capacity / 2, bucket.tokens); - assertEq(BLOCK_TIME, bucket.lastUpdated); - - uint256 warpTime = 4; - vm.warp(BLOCK_TIME + warpTime); - - bucket = s_rateLimiter.currentRateLimiterState(); - - assertEq(s_config.rate, bucket.rate); - assertEq(s_config.capacity, bucket.capacity); - assertEq(s_config.capacity / 2 + warpTime * s_config.rate, bucket.tokens); - assertEq(BLOCK_TIME + warpTime, bucket.lastUpdated); - - vm.warp(BLOCK_TIME + warpTime * 100); - - // Bucket overflow - bucket = s_rateLimiter.currentRateLimiterState(); - assertEq(s_config.capacity, bucket.tokens); - } - - // Reverts - - function test_TimeUnderflow_Revert() public { - vm.warp(BLOCK_TIME - 1); - - vm.expectRevert(stdError.arithmeticError); - s_rateLimiter.currentRateLimiterState(); - } -} - -contract AggregateTokenLimiter_setRateLimiterConfig is AggregateTokenLimiterSetup { - function test_Owner_Success() public { - setConfig(); - } - - function test_TokenLimitAdmin_Success() public { - vm.startPrank(ADMIN); - setConfig(); - } - - function setConfig() private { - RateLimiter.TokenBucket memory bucket = s_rateLimiter.currentRateLimiterState(); - assertEq(s_config.rate, bucket.rate); - assertEq(s_config.capacity, bucket.capacity); - - if (bucket.isEnabled) { - s_config = RateLimiter.Config({isEnabled: false, rate: 0, capacity: 0}); - } else { - s_config = RateLimiter.Config({isEnabled: true, rate: 100, capacity: 200}); - } - - vm.expectEmit(); - emit RateLimiter.ConfigChanged(s_config); - - s_rateLimiter.setRateLimiterConfig(s_config); - - bucket = s_rateLimiter.currentRateLimiterState(); - assertEq(s_config.rate, bucket.rate); - assertEq(s_config.capacity, bucket.capacity); - assertEq(s_config.isEnabled, bucket.isEnabled); - } - - // Reverts - - function test_OnlyOnlyCallableByAdminOrOwner_Revert() public { - vm.startPrank(STRANGER); - - vm.expectRevert(RateLimiter.OnlyCallableByAdminOrOwner.selector); - - s_rateLimiter.setRateLimiterConfig(s_config); - } -} - -contract AggregateTokenLimiter_rateLimitValue is AggregateTokenLimiterSetup { - function test_RateLimitValueSuccess_gas() public { - vm.pauseGasMetering(); - // start from blocktime that does not equal rate limiter init timestamp - vm.warp(BLOCK_TIME + 1); - - // 15 (tokens) * 4 (price) * 2 (number of times) > 100 (capacity) - uint256 numberOfTokens = 15; - uint256 value = (numberOfTokens * TOKEN_PRICE) / 1e18; - - vm.expectEmit(); - emit RateLimiter.TokensConsumed(value); - - vm.resumeGasMetering(); - s_rateLimiter.rateLimitValue(value); - vm.pauseGasMetering(); - - // Get the updated bucket status - RateLimiter.TokenBucket memory bucket = s_rateLimiter.currentRateLimiterState(); - // Assert the proper value has been taken out of the bucket - assertEq(bucket.capacity - value, bucket.tokens); - - // Since value * 2 > bucket.capacity we cannot take it out twice. - // Expect a revert when we try, with a wait time. - uint256 waitTime = 4; - vm.expectRevert( - abi.encodeWithSelector(RateLimiter.AggregateValueRateLimitReached.selector, waitTime, bucket.tokens) - ); - s_rateLimiter.rateLimitValue(value); - - // Move the block time forward by 10 so the bucket refills by 10 * rate - vm.warp(BLOCK_TIME + 1 + waitTime); - - // The bucket has filled up enough so we can take out more tokens - s_rateLimiter.rateLimitValue(value); - bucket = s_rateLimiter.currentRateLimiterState(); - assertEq(bucket.capacity - value + waitTime * s_config.rate - value, bucket.tokens); - vm.resumeGasMetering(); - } - - // Reverts - - function test_AggregateValueMaxCapacityExceeded_Revert() public { - RateLimiter.TokenBucket memory bucket = s_rateLimiter.currentRateLimiterState(); - - uint256 numberOfTokens = 100; - uint256 value = (numberOfTokens * TOKEN_PRICE) / 1e18; - - vm.expectRevert( - abi.encodeWithSelector( - RateLimiter.AggregateValueMaxCapacityExceeded.selector, bucket.capacity, (numberOfTokens * TOKEN_PRICE) / 1e18 - ) - ); - s_rateLimiter.rateLimitValue(value); - } -} - -contract AggregateTokenLimiter_getTokenValue is AggregateTokenLimiterSetup { - function test_GetTokenValue_Success() public view { - uint256 numberOfTokens = 10; - Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({token: TOKEN, amount: 10}); - uint256 value = s_rateLimiter.getTokenValue(tokenAmount, IPriceRegistry(address(s_feeQuoter))); - assertEq(value, (numberOfTokens * TOKEN_PRICE) / 1e18); - } - - // Reverts - function test_NoTokenPrice_Reverts() public { - address tokenWithNoPrice = makeAddr("Token with no price"); - Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({token: tokenWithNoPrice, amount: 10}); - - vm.expectRevert(abi.encodeWithSelector(AggregateRateLimiter.PriceNotFoundForToken.selector, tokenWithNoPrice)); - s_rateLimiter.getTokenValue(tokenAmount, IPriceRegistry(address(s_feeQuoter))); - } -} diff --git a/contracts/src/v0.8/ccip/test/arm/ARMProxy_standalone.t.sol b/contracts/src/v0.8/ccip/test/rmn/ARMProxy.t.sol similarity index 61% rename from contracts/src/v0.8/ccip/test/arm/ARMProxy_standalone.t.sol rename to contracts/src/v0.8/ccip/test/rmn/ARMProxy.t.sol index 4f3e96fafa2..efcdfd82277 100644 --- a/contracts/src/v0.8/ccip/test/arm/ARMProxy_standalone.t.sol +++ b/contracts/src/v0.8/ccip/test/rmn/ARMProxy.t.sol @@ -1,9 +1,37 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; -import {ARMProxy} from "../../ARMProxy.sol"; +import {IRMN} from "../../interfaces/IRMN.sol"; + +import {ARMProxy} from "../../rmn/ARMProxy.sol"; +import {MockRMN} from "../mocks/MockRMN.sol"; import {Test} from "forge-std/Test.sol"; +contract ARMProxyTest is Test { + MockRMN internal s_mockRMN; + ARMProxy internal s_armProxy; + + function setUp() public virtual { + s_mockRMN = new MockRMN(); + s_armProxy = new ARMProxy(address(s_mockRMN)); + } + + function test_ARMIsCursed_Success() public { + s_armProxy.setARM(address(s_mockRMN)); + assertFalse(IRMN(address(s_armProxy)).isCursed()); + s_mockRMN.setGlobalCursed(true); + assertTrue(IRMN(address(s_armProxy)).isCursed()); + } + + function test_ARMCallRevertReasonForwarded() public { + bytes memory err = bytes("revert"); + s_mockRMN.setIsCursedRevert(err); + s_armProxy.setARM(address(s_mockRMN)); + vm.expectRevert(abi.encodeWithSelector(MockRMN.CustomError.selector, err)); + IRMN(address(s_armProxy)).isCursed(); + } +} + contract ARMProxyStandaloneTest is Test { address internal constant EMPTY_ADDRESS = address(0x1); address internal constant OWNER_ADDRESS = 0xC0ffeeEeC0fFeeeEc0ffeEeEc0ffEEEEC0FfEEee; @@ -40,33 +68,6 @@ contract ARMProxyStandaloneTest is Test { s_armProxy.setARM(address(0x0)); } - /* - function test_Fuzz_ARMCall(bool expectedSuccess, bytes memory call, bytes memory ret) public { - // filter out calls to functions that will be handled on the ARMProxy instead - // of the underlying ARM contract - vm.assume( - call.length < 4 || - (bytes4(call) != s_armProxy.getARM.selector && - bytes4(call) != s_armProxy.setARM.selector && - bytes4(call) != s_armProxy.owner.selector && - bytes4(call) != s_armProxy.acceptOwnership.selector && - bytes4(call) != s_armProxy.transferOwnership.selector && - bytes4(call) != s_armProxy.typeAndVersion.selector) - ); - - if (expectedSuccess) { - vm.mockCall(MOCK_RMN_ADDRESS, 0, call, ret); - } else { - vm.mockCallRevert(MOCK_RMN_ADDRESS, 0, call, ret); - } - (bool actualSuccess, bytes memory result) = address(s_armProxy).call(call); - vm.clearMockedCalls(); - - assertEq(result, ret); - assertEq(expectedSuccess, actualSuccess); - } - */ - function test_ARMCallEmptyContractRevert() public { vm.prank(OWNER_ADDRESS); s_armProxy.setARM(EMPTY_ADDRESS); // No code at address 1, should revert. diff --git a/contracts/src/v0.8/ccip/test/router/Router.t.sol b/contracts/src/v0.8/ccip/test/router/Router.t.sol index 95d3c2f293f..85a70935306 100644 --- a/contracts/src/v0.8/ccip/test/router/Router.t.sol +++ b/contracts/src/v0.8/ccip/test/router/Router.t.sol @@ -9,22 +9,22 @@ import {IWrappedNative} from "../../interfaces/IWrappedNative.sol"; import {Router} from "../../Router.sol"; import {Client} from "../../libraries/Client.sol"; import {Internal} from "../../libraries/Internal.sol"; -import {EVM2EVMOnRamp} from "../../onRamp/EVM2EVMOnRamp.sol"; +import {OnRamp} from "../../onRamp/OnRamp.sol"; import {MaybeRevertMessageReceiver} from "../helpers/receivers/MaybeRevertMessageReceiver.sol"; -import {EVM2EVMOffRampSetup} from "../offRamp/EVM2EVMOffRampSetup.t.sol"; -import {EVM2EVMOnRampSetup} from "../onRamp/EVM2EVMOnRampSetup.t.sol"; +import {OffRampSetup} from "../offRamp/OffRampSetup.t.sol"; +import {OnRampSetup} from "../onRamp/OnRampSetup.t.sol"; import {RouterSetup} from "../router/RouterSetup.t.sol"; import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -contract Router_constructor is EVM2EVMOnRampSetup { +contract Router_constructor is OnRampSetup { function test_Constructor_Success() public view { assertEq("Router 1.2.0", s_sourceRouter.typeAndVersion()); assertEq(OWNER, s_sourceRouter.owner()); } } -contract Router_recoverTokens is EVM2EVMOnRampSetup { +contract Router_recoverTokens is OnRampSetup { function test_RecoverTokens_Success() public { // Assert we can recover sourceToken IERC20 token = IERC20(s_sourceTokens[0]); @@ -72,7 +72,7 @@ contract Router_recoverTokens is EVM2EVMOnRampSetup { } } -contract Router_ccipSend is EVM2EVMOnRampSetup { +contract Router_ccipSend is OnRampSetup { event Burned(address indexed sender, uint256 amount); function test_CCIPSendLinkFeeOneTokenSuccess_gas() public { @@ -95,16 +95,16 @@ contract Router_ccipSend is EVM2EVMOnRampSetup { vm.expectEmit(); emit Burned(address(s_onRamp), message.tokenAmounts[0].amount); - Internal.EVM2EVMMessage memory msgEvent = _messageToEvent(message, 1, 1, expectedFee, OWNER); + Internal.EVM2AnyRampMessage memory msgEvent = _messageToEvent(message, 1, 1, expectedFee, OWNER); vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(msgEvent); + emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, msgEvent.header.sequenceNumber, msgEvent); vm.resumeGasMetering(); bytes32 messageId = s_sourceRouter.ccipSend(DEST_CHAIN_SELECTOR, message); vm.pauseGasMetering(); - assertEq(msgEvent.messageId, messageId); + assertEq(msgEvent.header.messageId, messageId); // Assert the user balance is lowered by the tokenAmounts sent and the fee amount uint256 expectedBalance = balanceBefore - (message.tokenAmounts[0].amount); assertEq(expectedBalance, sourceToken1.balanceOf(OWNER)); @@ -118,96 +118,87 @@ contract Router_ccipSend is EVM2EVMOnRampSetup { uint256 expectedFee = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, message); assertGt(expectedFee, 0); - Internal.EVM2EVMMessage memory msgEvent = _messageToEvent(message, 1, 1, expectedFee, OWNER); + Internal.EVM2AnyRampMessage memory msgEvent = _messageToEvent(message, 1, 1, expectedFee, OWNER); vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(msgEvent); + emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, msgEvent.header.sequenceNumber, msgEvent); vm.resumeGasMetering(); bytes32 messageId = s_sourceRouter.ccipSend(DEST_CHAIN_SELECTOR, message); vm.pauseGasMetering(); - assertEq(msgEvent.messageId, messageId); + assertEq(msgEvent.header.messageId, messageId); vm.resumeGasMetering(); } - function test_CCIPSendNativeFeeOneTokenSuccess_gas() public { + function test_ccipSend_nativeFeeOneTokenSuccess_gas() public { vm.pauseGasMetering(); Client.EVM2AnyMessage memory message = _generateEmptyMessage(); IERC20 sourceToken1 = IERC20(s_sourceTokens[1]); sourceToken1.approve(address(s_sourceRouter), 2 ** 64); + uint256 balanceBefore = sourceToken1.balanceOf(OWNER); + message.tokenAmounts = new Client.EVMTokenAmount[](1); message.tokenAmounts[0].amount = 2 ** 64; message.tokenAmounts[0].token = s_sourceTokens[1]; + // Native fees will be wrapped so we need to calculate the event with + // the wrapped native feeCoin address. + message.feeToken = s_sourceRouter.getWrappedNative(); uint256 expectedFee = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, message); assertGt(expectedFee, 0); - uint256 balanceBefore = sourceToken1.balanceOf(OWNER); + Internal.EVM2AnyRampMessage memory msgEvent = _messageToEvent(message, 1, 1, expectedFee, OWNER); + msgEvent.feeValueJuels = expectedFee * s_sourceTokenPrices[1] / s_sourceTokenPrices[0]; + message.feeToken = address(0); // Assert that the tokens are burned vm.expectEmit(); emit Burned(address(s_onRamp), message.tokenAmounts[0].amount); - // Native fees will be wrapped so we need to calculate the event with - // the wrapped native feeCoin address. - message.feeToken = s_sourceRouter.getWrappedNative(); - Internal.EVM2EVMMessage memory msgEvent = _messageToEvent(message, 1, 1, expectedFee, OWNER); - // Set it to address(0) to indicate native - message.feeToken = address(0); - vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(msgEvent); + emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, msgEvent.header.sequenceNumber, msgEvent); vm.resumeGasMetering(); bytes32 messageId = s_sourceRouter.ccipSend{value: expectedFee}(DEST_CHAIN_SELECTOR, message); vm.pauseGasMetering(); - assertEq(msgEvent.messageId, messageId); + assertEq(msgEvent.header.messageId, messageId); // Assert the user balance is lowered by the tokenAmounts sent and the fee amount uint256 expectedBalance = balanceBefore - (message.tokenAmounts[0].amount); assertEq(expectedBalance, sourceToken1.balanceOf(OWNER)); vm.resumeGasMetering(); } - function test_CCIPSendNativeFeeNoTokenSuccess_gas() public { + function test_ccipSend_nativeFeeNoTokenSuccess_gas() public { vm.pauseGasMetering(); Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - uint256 expectedFee = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, message); - assertGt(expectedFee, 0); - // Native fees will be wrapped so we need to calculate the event with // the wrapped native feeCoin address. message.feeToken = s_sourceRouter.getWrappedNative(); - Internal.EVM2EVMMessage memory msgEvent = _messageToEvent(message, 1, 1, expectedFee, OWNER); + uint256 expectedFee = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, message); + assertGt(expectedFee, 0); + + Internal.EVM2AnyRampMessage memory msgEvent = _messageToEvent(message, 1, 1, expectedFee, OWNER); + msgEvent.feeValueJuels = expectedFee * s_sourceTokenPrices[1] / s_sourceTokenPrices[0]; // Set it to address(0) to indicate native message.feeToken = address(0); vm.expectEmit(); - emit EVM2EVMOnRamp.CCIPSendRequested(msgEvent); + emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, msgEvent.header.sequenceNumber, msgEvent); vm.resumeGasMetering(); bytes32 messageId = s_sourceRouter.ccipSend{value: expectedFee}(DEST_CHAIN_SELECTOR, message); vm.pauseGasMetering(); - assertEq(msgEvent.messageId, messageId); + assertEq(msgEvent.header.messageId, messageId); // Assert the user balance is lowered by the tokenAmounts sent and the fee amount vm.resumeGasMetering(); } function test_NonLinkFeeToken_Success() public { - EVM2EVMOnRamp.FeeTokenConfigArgs[] memory feeTokenConfigArgs = new EVM2EVMOnRamp.FeeTokenConfigArgs[](1); - feeTokenConfigArgs[0] = EVM2EVMOnRamp.FeeTokenConfigArgs({ - token: s_sourceTokens[1], - networkFeeUSDCents: 1, - gasMultiplierWeiPerEth: 108e16, - premiumMultiplierWeiPerEth: 1e18, - enabled: true - }); - s_onRamp.setFeeTokenConfig(feeTokenConfigArgs); - address[] memory feeTokens = new address[](1); feeTokens[0] = s_sourceTokens[1]; s_feeQuoter.applyFeeTokensUpdates(feeTokens, new address[](0)); @@ -251,50 +242,6 @@ contract Router_ccipSend is EVM2EVMOnRampSetup { s_sourceRouter.ccipSend(DEST_CHAIN_SELECTOR, message); } - // Since sending with zero fees is a legitimate use case for some destination - // chains, e.g. private chains, we want to make sure that we can still send even - // when the configured fee is 0. - function test_ZeroFeeAndGasPrice_Success() public { - // Configure a new fee token that has zero gas and zero fees but is still - // enabled and valid to pay with. - address feeTokenWithZeroFeeAndGas = s_sourceTokens[1]; - - // Set the new token as feeToken - address[] memory feeTokens = new address[](1); - feeTokens[0] = feeTokenWithZeroFeeAndGas; - s_feeQuoter.applyFeeTokensUpdates(feeTokens, new address[](0)); - - // Update the price of the newly set feeToken - Internal.PriceUpdates memory priceUpdates = _getSingleTokenPriceUpdateStruct(feeTokenWithZeroFeeAndGas, 2_000 ether); - priceUpdates.gasPriceUpdates = new Internal.GasPriceUpdate[](1); - priceUpdates.gasPriceUpdates[0] = - Internal.GasPriceUpdate({destChainSelector: DEST_CHAIN_SELECTOR, usdPerUnitGas: 0}); - - s_feeQuoter.updatePrices(priceUpdates); - - // Set the feeToken args on the onRamp - EVM2EVMOnRamp.FeeTokenConfigArgs[] memory feeTokenConfigArgs = new EVM2EVMOnRamp.FeeTokenConfigArgs[](1); - feeTokenConfigArgs[0] = EVM2EVMOnRamp.FeeTokenConfigArgs({ - token: s_sourceTokens[1], - networkFeeUSDCents: 0, - gasMultiplierWeiPerEth: 108e16, - premiumMultiplierWeiPerEth: 1e18, - enabled: true - }); - - s_onRamp.setFeeTokenConfig(feeTokenConfigArgs); - - // Send a message with the new feeToken - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.feeToken = feeTokenWithZeroFeeAndGas; - - // Fee should be 0 and sending should not revert - uint256 fee = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, message); - assertEq(fee, 0); - - s_sourceRouter.ccipSend(DEST_CHAIN_SELECTOR, message); - } - // Reverts function test_WhenNotHealthy_Revert() public { @@ -313,38 +260,6 @@ contract Router_ccipSend is EVM2EVMOnRampSetup { s_sourceRouter.ccipSend(wrongChain, message); } - function test_Fuzz_UnsupportedFeeToken_Reverts(address wrongFeeToken) public { - // We have three fee tokens set, all others should revert. - vm.assume(address(s_sourceFeeToken) != wrongFeeToken); - vm.assume(address(s_sourceRouter.getWrappedNative()) != wrongFeeToken); - vm.assume(address(0) != wrongFeeToken); - - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.feeToken = wrongFeeToken; - - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOnRamp.NotAFeeToken.selector, wrongFeeToken)); - - s_sourceRouter.ccipSend(DEST_CHAIN_SELECTOR, message); - } - - function test_Fuzz_UnsupportedToken_Reverts(address wrongToken) public { - for (uint256 i = 0; i < s_sourceTokens.length; ++i) { - vm.assume(address(s_sourceTokens[i]) != wrongToken); - } - - for (uint256 i = 0; i < s_destTokens.length; ++i) { - vm.assume(address(s_destTokens[i]) != wrongToken); - } - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); - tokenAmounts[0] = Client.EVMTokenAmount({token: wrongToken, amount: 1}); - message.tokenAmounts = tokenAmounts; - - vm.expectRevert(abi.encodeWithSelector(EVM2EVMOnRamp.UnsupportedToken.selector, wrongToken)); - - s_sourceRouter.ccipSend(DEST_CHAIN_SELECTOR, message); - } - function test_FeeTokenAmountTooLow_Revert() public { Client.EVM2AnyMessage memory message = _generateEmptyMessage(); IERC20(s_sourceTokens[0]).approve(address(s_sourceRouter), 0); @@ -377,8 +292,6 @@ contract Router_ccipSend is EVM2EVMOnRampSetup { // Include insufficient, should also revert vm.stopPrank(); - s_onRamp.getFeeTokenConfig(s_sourceRouter.getWrappedNative()); - hoax(address(1), 1); vm.expectRevert(IRouterClient.InsufficientFeeTokenAmount.selector); s_sourceRouter.ccipSend{value: 1}(DEST_CHAIN_SELECTOR, message); @@ -395,7 +308,7 @@ contract Router_applyRampUpdates is RouterSetup { MaybeRevertMessageReceiver internal s_receiver; function setUp() public virtual override(RouterSetup) { - RouterSetup.setUp(); + super.setUp(); s_receiver = new MaybeRevertMessageReceiver(false); } @@ -667,7 +580,7 @@ contract Router_applyRampUpdates is RouterSetup { } } -contract Router_setWrappedNative is EVM2EVMOnRampSetup { +contract Router_setWrappedNative is OnRampSetup { function test_Fuzz_SetWrappedNative_Success(address wrappedNative) public { s_sourceRouter.setWrappedNative(wrappedNative); assertEq(wrappedNative, s_sourceRouter.getWrappedNative()); @@ -681,16 +594,16 @@ contract Router_setWrappedNative is EVM2EVMOnRampSetup { } } -contract Router_getSupportedTokens is EVM2EVMOnRampSetup { +contract Router_getSupportedTokens is OnRampSetup { function test_GetSupportedTokens_Revert() public { - vm.expectRevert(EVM2EVMOnRamp.GetSupportedTokensFunctionalityRemovedCheckAdminRegistry.selector); + vm.expectRevert(OnRamp.GetSupportedTokensFunctionalityRemovedCheckAdminRegistry.selector); s_onRamp.getSupportedTokens(DEST_CHAIN_SELECTOR); } } -contract Router_routeMessage is EVM2EVMOffRampSetup { +contract Router_routeMessage is OffRampSetup { function setUp() public virtual override { - EVM2EVMOffRampSetup.setUp(); + super.setUp(); vm.startPrank(address(s_offRamp)); } @@ -698,7 +611,7 @@ contract Router_routeMessage is EVM2EVMOffRampSetup { return ((gasleft() - 2 * (16 * callDataLength + GAS_FOR_CALL_EXACT_CHECK)) * 62) / 64; } - function test_ManualExec_Success() public { + function test_routeMessage_ManualExec_Success() public { Client.Any2EVMMessage memory message = _generateReceiverMessage(SOURCE_CHAIN_SELECTOR); // Manuel execution cannot run out of gas @@ -713,7 +626,7 @@ contract Router_routeMessage is EVM2EVMOffRampSetup { assertGt(gasUsed, 3_000); } - function test_ExecutionEvent_Success() public { + function test_routeMessage_ExecutionEvent_Success() public { Client.Any2EVMMessage memory message = _generateReceiverMessage(SOURCE_CHAIN_SELECTOR); // Should revert with reason bytes memory realError1 = new bytes(2); @@ -796,7 +709,7 @@ contract Router_routeMessage is EVM2EVMOffRampSetup { assertGt(gasUsed, 3_000); } - function test_Fuzz_ExecutionEvent_Success(bytes calldata error) public { + function testFuzz_routeMessage_ExecutionEvent_Success(bytes calldata error) public { Client.Any2EVMMessage memory message = _generateReceiverMessage(SOURCE_CHAIN_SELECTOR); s_reverting_receiver.setErr(error); @@ -840,7 +753,7 @@ contract Router_routeMessage is EVM2EVMOffRampSetup { assertEq(expectedRetData, retData); } - function test_AutoExec_Success() public { + function test_routeMessage_AutoExec_Success() public { (bool success,,) = s_destRouter.routeMessage( _generateReceiverMessage(SOURCE_CHAIN_SELECTOR), GAS_FOR_CALL_EXACT_CHECK, 100_000, address(s_receiver) ); @@ -856,7 +769,7 @@ contract Router_routeMessage is EVM2EVMOffRampSetup { } // Reverts - function test_OnlyOffRamp_Revert() public { + function test_routeMessage_OnlyOffRamp_Revert() public { vm.stopPrank(); vm.startPrank(STRANGER); @@ -866,7 +779,7 @@ contract Router_routeMessage is EVM2EVMOffRampSetup { ); } - function test_WhenNotHealthy_Revert() public { + function test_routeMessage_WhenNotHealthy_Revert() public { s_mockRMN.setGlobalCursed(true); vm.expectRevert(Router.BadARMSignal.selector); s_destRouter.routeMessage( @@ -875,7 +788,7 @@ contract Router_routeMessage is EVM2EVMOffRampSetup { } } -contract Router_getFee is EVM2EVMOnRampSetup { +contract Router_getFee is OnRampSetup { function test_GetFeeSupportedChain_Success() public view { Client.EVM2AnyMessage memory message = _generateEmptyMessage(); uint256 expectedFee = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, message); diff --git a/contracts/src/v0.8/keystone/CapabilitiesRegistry.sol b/contracts/src/v0.8/keystone/CapabilitiesRegistry.sol index 4f4ff6f3280..2e9211f983a 100644 --- a/contracts/src/v0.8/keystone/CapabilitiesRegistry.sol +++ b/contracts/src/v0.8/keystone/CapabilitiesRegistry.sol @@ -1,33 +1,30 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.24; -import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol"; +import {ITypeAndVersion} from "../shared/interfaces/ITypeAndVersion.sol"; +import {ICapabilityConfiguration} from "./interfaces/ICapabilityConfiguration.sol"; + import {OwnerIsCreator} from "../shared/access/OwnerIsCreator.sol"; + import {EnumerableSet} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; import {ERC165Checker} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/ERC165Checker.sol"; -import {ICapabilityConfiguration} from "./interfaces/ICapabilityConfiguration.sol"; -/// @notice CapabilitiesRegistry is used to manage Nodes (including their links to Node -/// Operators), Capabilities, and DONs (Decentralized Oracle Networks) which are -/// sets of nodes that support those Capabilities. -/// @dev The contract currently stores the entire state of Node Operators, Nodes, -/// Capabilities and DONs in the contract and requires a full state migration -/// if an upgrade is ever required. The team acknowledges this and is fine -/// reconfiguring the upgraded contract in the future so as to not add extra -/// complexity to this current version. -contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { +/// @notice CapabilitiesRegistry is used to manage Nodes (including their links to Node Operators), Capabilities, +/// and DONs (Decentralized Oracle Networks) which are sets of nodes that support those Capabilities. +/// @dev The contract currently stores the entire state of Node Operators, Nodes, Capabilities and DONs in the +/// contract and requires a full state migration if an upgrade is ever required. The team acknowledges this and is +/// fine reconfiguring the upgraded contract in the future so as to not add extra complexity to this current version. +contract CapabilitiesRegistry is OwnerIsCreator, ITypeAndVersion { // Add the library methods using EnumerableSet for EnumerableSet.Bytes32Set; using EnumerableSet for EnumerableSet.UintSet; struct NodeOperator { - /// @notice The address of the admin that can manage a node - /// operator + /// @notice The address of the admin that can manage a node operator address admin; /// @notice Human readable name of a Node Operator managing the node - /// @dev The contract does not validate the length or characters of the - /// node operator name because a trusted admin will supply these names. - /// We reduce gas costs by omitting these checks on-chain. + /// @dev The contract does not validate the length or characters of the node operator name because + /// a trusted admin will supply these names. We reduce gas costs by omitting these checks on-chain. string name; } @@ -36,9 +33,8 @@ contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { uint32 nodeOperatorId; /// @notice The signer address for application-layer message verification. bytes32 signer; - /// @notice This is an Ed25519 public key that is used to identify a node. - /// This key is guaranteed to be unique in the CapabilitiesRegistry. It is - /// used to identify a node in the the P2P network. + /// @notice This is an Ed25519 public key that is used to identify a node. This key is guaranteed to + /// be unique in the CapabilitiesRegistry. It is used to identify a node in the the P2P network. bytes32 p2pId; /// @notice Public key used to encrypt secrets for this node bytes32 encryptionPublicKey; @@ -56,17 +52,15 @@ contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { uint32 workflowDONId; /// @notice The signer address for application-layer message verification. bytes32 signer; - /// @notice This is an Ed25519 public key that is used to identify a node. - /// This key is guaranteed to be unique in the CapabilitiesRegistry. It is - /// used to identify a node in the the P2P network. + /// @notice This is an Ed25519 public key that is used to identify a node. This key is guaranteed + /// to be unique in the CapabilitiesRegistry. It is used to identify a node in the the P2P network. bytes32 p2pId; /// @notice Public key used to encrypt secrets for this node bytes32 encryptionPublicKey; /// @notice The list of hashed capability IDs supported by the node bytes32[] hashedCapabilityIds; - /// @notice The list of capabilities DON Ids supported by the node. A node - /// can belong to multiple capabilities DONs. This list does not include a - /// Workflow DON id if the node belongs to one. + /// @notice The list of capabilities DON Ids supported by the node. A node can belong to multiple + /// capabilities DONs. This list does not include a Workflow DON id if the node belongs to one. uint256[] capabilitiesDONIds; } @@ -80,37 +74,31 @@ contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { /// only belong to one DON that accepts Workflows. uint32 workflowDONId; /// @notice The signer address for application-layer message verification. - /// @dev This key is guaranteed to be unique in the CapabilitiesRegistry - /// as a signer address can only belong to one node. - /// @dev This should be the ABI encoded version of the node's address. - /// I.e 0x0000address. The Capability Registry does not store it as an address so that - /// non EVM chains with addresses greater than 20 bytes can be supported + /// @dev This key is guaranteed to be unique in the CapabilitiesRegistry as a signer + /// address can only belong to one node. + /// @dev This should be the ABI encoded version of the node's address. I.e 0x0000address. The Capability Registry + /// does not store it as an address so that non EVM chains with addresses greater than 20 bytes can be supported /// in the future. bytes32 signer; - /// @notice This is an Ed25519 public key that is used to identify a node. - /// This key is guaranteed to be unique in the CapabilitiesRegistry. It is - /// used to identify a node in the the P2P network. + /// @notice This is an Ed25519 public key that is used to identify a node. This key is guaranteed + /// to be unique in the CapabilitiesRegistry. It is used to identify a node in the the P2P network. bytes32 p2pId; /// @notice Public key used to encrypt secrets for this node bytes32 encryptionPublicKey; /// @notice The node's supported capabilities - /// @dev This is stored as a map so that we can easily update to a set of - /// new capabilities by incrementing the configCount and creating a - /// new set of supported capability IDs + /// @dev This is stored as a map so that we can easily update to a set of new capabilities by + /// incrementing the configCount and creating a new set of supported capability IDs mapping(uint32 configCount => EnumerableSet.Bytes32Set capabilityId) supportedHashedCapabilityIds; - /// @notice The list of capabilities DON Ids supported by the node. A node - /// can belong to multiple capabilities DONs. This list does not include a - /// Workflow DON id if the node belongs to one. + /// @notice The list of capabilities DON Ids supported by the node. A node can belong to multiple + /// capabilities DONs. This list does not include a Workflow DON id if the node belongs to one. EnumerableSet.UintSet capabilitiesDONIds; } - /// @notice CapabilityResponseType indicates whether remote response requires - // aggregation or is an already aggregated report. There are multiple - // possible ways to aggregate. - /// @dev REPORT response type receives signatures together with the response that - /// is used to verify the data. OBSERVATION_IDENTICAL just receives data without - /// signatures and waits for some number of observations before proceeeding to - /// the next step + /// @notice CapabilityResponseType indicates whether remote response requires aggregation or is + /// an already aggregated report. There are multiple possible ways to aggregate. + /// @dev REPORT response type receives signatures together with the response that is used to verify the data. + /// OBSERVATION_IDENTICAL just receives data without signatures and waits for some number of observations before + /// proceeding to the next step enum CapabilityResponseType { // No additional aggregation is needed on the remote response. REPORT, @@ -130,7 +118,8 @@ contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { struct Capability { /// @notice The partially qualified ID for the capability. /// @dev Given the following capability ID: {name}:{label1_key}_{label1_value}:{label2_key}_{label2_value}@{version} - // Then we denote the `labelledName` as the `{name}:{label1_key}_{label1_value}:{label2_key}_{label2_value}` portion of the ID. + /// Then we denote the `labelledName` as the `{name}:{label1_key}_{label1_value}:{label2_key}_{label2_value}` + /// portion of the ID. /// /// Ex. id = "data-streams-reports:chain:ethereum@1.0.0" /// labelledName = "data-streams-reports:chain:ethereum" @@ -141,22 +130,18 @@ contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { /// @notice CapabilityType indicates the type of capability which determines /// where the capability can be used in a Workflow Spec. CapabilityType capabilityType; - /// @notice CapabilityResponseType indicates whether remote response requires - // aggregation or is an already aggregated report. There are multiple - // possible ways to aggregate. + /// @notice CapabilityResponseType indicates whether remote response requires aggregation or is an + /// already aggregated report. There are multiple possible ways to aggregate. CapabilityResponseType responseType; - /// @notice An address to the capability configuration contract. Having this defined - // on a capability enforces consistent configuration across DON instances - // serving the same capability. Configuration contract MUST implement - // CapabilityConfigurationContractInterface. - // + /// @notice An address to the capability configuration contract. Having this defined on a capability enforces + /// consistent configuration across DON instances serving the same capability. Configuration contract MUST implement + /// CapabilityConfigurationContractInterface. + /// /// @dev The main use cases are: - // 1) Sharing capability configuration across DON instances - // 2) Inspect and modify on-chain configuration without off-chain - // capability code. - // - // It is not recommended to store configuration which requires knowledge of - // the DON membership. + /// 1) Sharing capability configuration across DON instances + /// 2) Inspect and modify on-chain configuration without off-chain capability code. + /// + /// It is not recommended to store configuration which requires knowledge of the DON membership. address configurationContract; } @@ -165,7 +150,8 @@ contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { bytes32 hashedId; /// @notice The partially qualified ID for the capability. /// @dev Given the following capability ID: {name}:{label1_key}_{label1_value}:{label2_key}_{label2_value}@{version} - // Then we denote the `labelledName` as the `{name}:{label1_key}_{label1_value}:{label2_key}_{label2_value}` portion of the ID. + /// Then we denote the `labelledName` as the `{name}:{label1_key}_{label1_value}:{label2_key}_{label2_value}` + /// portion of the ID. /// /// Ex. id = "data-streams-reports:chain:ethereum@1.0.0" /// labelledName = "data-streams-reports:chain:ethereum" @@ -176,22 +162,18 @@ contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { /// @notice CapabilityType indicates the type of capability which determines /// where the capability can be used in a Workflow Spec. CapabilityType capabilityType; - /// @notice CapabilityResponseType indicates whether remote response requires - // aggregation or is an already aggregated report. There are multiple - // possible ways to aggregate. + /// @notice CapabilityResponseType indicates whether remote response requires aggregation + /// or is an already aggregated report. There are multiple possible ways to aggregate. CapabilityResponseType responseType; - /// @notice An address to the capability configuration contract. Having this defined - // on a capability enforces consistent configuration across DON instances - // serving the same capability. Configuration contract MUST implement - // CapabilityConfigurationContractInterface. - // + /// @notice An address to the capability configuration contract. Having this defined on a capability enforces + /// consistent configuration across DON instances serving the same capability. Configuration contract MUST implement + /// CapabilityConfigurationContractInterface. + /// /// @dev The main use cases are: - // 1) Sharing capability configuration across DON instances - // 2) Inspect and modify on-chain configuration without off-chain - // capability code. - // - // It is not recommended to store configuration which requires knowledge of - // the DON membership. + /// 1) Sharing capability configuration across DON instances + /// 2) Inspect and modify on-chain configuration without off-chain capability code. + /// + /// It is not recommended to store configuration which requires knowledge of the DON membership. address configurationContract; /// @notice True if the capability is deprecated bool isDeprecated; @@ -202,14 +184,12 @@ contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { struct CapabilityConfiguration { /// @notice The capability Id bytes32 capabilityId; - /// @notice The capability config specific to a DON. This will be decoded - /// offchain + /// @notice The capability config specific to a DON. This will be decoded offchain bytes config; } struct DONCapabilityConfig { - /// @notice The set of p2pIds of nodes that belong to this DON. A node (the same - // p2pId) can belong to multiple DONs. + /// @notice The set of p2pIds of nodes that belong to this DON. A node (the same p2pId) can belong to multiple DONs. EnumerableSet.Bytes32Set nodes; /// @notice The set of capabilityIds bytes32[] capabilityIds; @@ -362,7 +342,7 @@ contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { error NodeOperatorDoesNotExist(uint32 nodeOperatorId); /// @notice This error is thrown when trying to remove a node that is still - /// part of a capabitlities DON + /// part of a capabilities DON /// @param donId The Id of the DON the node belongs to /// @param nodeP2PId The P2P Id of the node being removed error NodePartOfCapabilitiesDON(uint32 donId, bytes32 nodeP2PId); @@ -434,6 +414,8 @@ contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { /// @param hashedCapabilityId The hashed ID of the deprecated capability event CapabilityDeprecated(bytes32 indexed hashedCapabilityId); + string public constant override typeAndVersion = "CapabilitiesRegistry 1.0.0"; + /// @notice Mapping of capabilities mapping(bytes32 hashedCapabilityId => Capability capability) private s_capabilities; @@ -470,10 +452,6 @@ contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { /// @dev No getter for this as this is an implementation detail uint32 private s_nextDONId = 1; - function typeAndVersion() external pure override returns (string memory) { - return "CapabilitiesRegistry 1.0.0"; - } - /// @notice Adds a list of node operators /// @param nodeOperators List of node operators to add function addNodeOperators(NodeOperator[] calldata nodeOperators) external onlyOwner { @@ -641,6 +619,8 @@ contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { s_nodeSigners.add(node.signer); } + if (node.encryptionPublicKey == bytes32("")) revert InvalidNodeEncryptionPublicKey(node.encryptionPublicKey); + bytes32[] memory supportedHashedCapabilityIds = node.hashedCapabilityIds; if (supportedHashedCapabilityIds.length == 0) revert InvalidNodeCapabilities(supportedHashedCapabilityIds); @@ -678,6 +658,7 @@ contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { storedNode.nodeOperatorId = node.nodeOperatorId; storedNode.p2pId = node.p2pId; + storedNode.encryptionPublicKey = node.encryptionPublicKey; emit NodeUpdated(node.p2pId, node.nodeOperatorId, node.signer); } @@ -919,8 +900,7 @@ contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { /// @notice Sets the configuration for a DON /// @param nodes The nodes making up the DON - /// @param capabilityConfigurations The list of configurations for the - /// capabilities supported by the DON + /// @param capabilityConfigurations The list of configurations for the capabilities supported by the DON /// @param donParams The DON's parameters function _setDONConfig( bytes32[] calldata nodes, diff --git a/contracts/src/v0.8/keystone/KeystoneFeedsConsumer.sol b/contracts/src/v0.8/keystone/KeystoneFeedsConsumer.sol index 306b211f33e..447c979d405 100644 --- a/contracts/src/v0.8/keystone/KeystoneFeedsConsumer.sol +++ b/contracts/src/v0.8/keystone/KeystoneFeedsConsumer.sol @@ -1,10 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.24; -import {IERC165} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; -import {OwnerIsCreator} from "../shared/access/OwnerIsCreator.sol"; import {IReceiver} from "./interfaces/IReceiver.sol"; +import {OwnerIsCreator} from "../shared/access/OwnerIsCreator.sol"; + +import {IERC165} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; + contract KeystoneFeedsConsumer is IReceiver, OwnerIsCreator, IERC165 { event FeedReceived(bytes32 indexed feedId, uint224 price, uint32 timestamp); diff --git a/contracts/src/v0.8/keystone/KeystoneFeedsPermissionHandler.sol b/contracts/src/v0.8/keystone/KeystoneFeedsPermissionHandler.sol index 3223deeebe5..5a28aa5f96a 100644 --- a/contracts/src/v0.8/keystone/KeystoneFeedsPermissionHandler.sol +++ b/contracts/src/v0.8/keystone/KeystoneFeedsPermissionHandler.sol @@ -10,11 +10,11 @@ abstract contract KeystoneFeedsPermissionHandler is OwnerIsCreator { /// @notice Holds the details for permissions of a report /// @dev Workflow names and report names are stored as bytes to optimize for gas efficiency. struct Permission { - address forwarder; //───────────────╮ The address of the forwarder (20 bytes) - bytes10 workflowName; // │ The name of the workflow in bytes10 - bytes2 reportName; //───────────────╯ The name of the report in bytes2 - address workflowOwner; //──────────────╮ // The address of the workflow owner (20 bytes) - bool isAllowed; //─────────────────────╯// Whether the report is allowed or not (1 byte) + address forwarder; // ──────╮ The address of the forwarder (20 bytes) + bytes10 workflowName; // │ The name of the workflow in bytes10 + bytes2 reportName; // ──────╯ The name of the report in bytes2 + address workflowOwner; // ──╮ The address of the workflow owner (20 bytes) + bool isAllowed; // ─────────╯ Whether the report is allowed or not (1 byte) } /// @notice Event emitted when report permissions are set diff --git a/contracts/src/v0.8/keystone/KeystoneForwarder.sol b/contracts/src/v0.8/keystone/KeystoneForwarder.sol index c4511124cd2..3616667fc78 100644 --- a/contracts/src/v0.8/keystone/KeystoneForwarder.sol +++ b/contracts/src/v0.8/keystone/KeystoneForwarder.sol @@ -1,57 +1,50 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.24; -import {ERC165Checker} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/ERC165Checker.sol"; - +import {IReceiver} from "./interfaces/IReceiver.sol"; +import {IRouter} from "./interfaces/IRouter.sol"; import {ITypeAndVersion} from "../shared/interfaces/ITypeAndVersion.sol"; + import {OwnerIsCreator} from "../shared/access/OwnerIsCreator.sol"; -import {IReceiver} from "./interfaces/IReceiver.sol"; -import {IRouter} from "./interfaces/IRouter.sol"; +import {ERC165Checker} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/ERC165Checker.sol"; -/// @notice This is an entry point for `write_${chain}` Target capability. It -/// allows nodes to determine if reports have been processed (successfully or -/// not) in a decentralized and product-agnostic way by recording processed -/// reports. +/// @notice This is an entry point for `write_${chain}` Target capability. It allows nodes to +/// determine if reports have been processed (successfully or not) in a decentralized and +/// product-agnostic way by recording processed reports. contract KeystoneForwarder is OwnerIsCreator, ITypeAndVersion, IRouter { - /// @notice This error is returned when the report is shorter than - /// REPORT_METADATA_LENGTH, which is the minimum length of a report. + /// @notice This error is returned when the report is shorter than REPORT_METADATA_LENGTH, + /// which is the minimum length of a report. error InvalidReport(); - /// @notice This error is thrown whenever trying to set a config with a fault - /// tolerance of 0. + /// @notice This error is thrown whenever trying to set a config with a fault tolerance of 0. error FaultToleranceMustBePositive(); - /// @notice This error is thrown whenever configuration provides more signers - /// than the maximum allowed number. + /// @notice This error is thrown whenever configuration provides more signers than the maximum allowed number. /// @param numSigners The number of signers who have signed the report /// @param maxSigners The maximum number of signers that can sign a report error ExcessSigners(uint256 numSigners, uint256 maxSigners); - /// @notice This error is thrown whenever a configuration is provided with - /// less than the minimum number of signers. + /// @notice This error is thrown whenever a configuration is provided with less than the minimum number of signers. /// @param numSigners The number of signers provided /// @param minSigners The minimum number of signers expected error InsufficientSigners(uint256 numSigners, uint256 minSigners); - /// @notice This error is thrown whenever a duplicate signer address is - /// provided in the configuration. + /// @notice This error is thrown whenever a duplicate signer address is provided in the configuration. /// @param signer The signer address that was duplicated. error DuplicateSigner(address signer); - /// @notice This error is thrown whenever a report has an incorrect number of - /// signatures. + /// @notice This error is thrown whenever a report has an incorrect number of signatures. /// @param expected The number of signatures expected, F + 1 /// @param received The number of signatures received error InvalidSignatureCount(uint256 expected, uint256 received); - /// @notice This error is thrown whenever a report specifies a configuration that - /// does not exist. + /// @notice This error is thrown whenever a report specifies a configuration that does not exist. /// @param configId (uint64(donId) << 32) | configVersion error InvalidConfig(uint64 configId); - /// @notice This error is thrown whenever a signer address is not in the - /// configuration or when trying to set a zero address as a signer. + /// @notice This error is thrown whenever a signer address is not in the configuration or + /// when trying to set a zero address as a signer. /// @param signer The signer address that was not in the configuration error InvalidSigner(address signer); @@ -68,26 +61,17 @@ contract KeystoneForwarder is OwnerIsCreator, ITypeAndVersion, IRouter { struct Transmission { address transmitter; - // This is true if the receiver is not a contract or does not implement the - // `IReceiver` interface. + // This is true if the receiver is not a contract or does not implement the `IReceiver` interface. bool invalidReceiver; - // Whether the transmission attempt was successful. If `false`, the - // transmission can be retried with an increased gas limit. + // Whether the transmission attempt was successful. If `false`, the transmission can be retried + // with an increased gas limit. bool success; - // The amount of gas allocated for the `IReceiver.onReport` call. uint80 - // allows storing gas for known EVM block gas limits. - // Ensures that the minimum gas requested by the user is available during - // the transmission attempt. If the transmission fails (indicated by a - // `false` success state), it can be retried with an increased gas limit. + // The amount of gas allocated for the `IReceiver.onReport` call. uint80 allows storing gas for known EVM block + // gas limits. Ensures that the minimum gas requested by the user is available during the transmission attempt. + // If the transmission fails (indicated by a `false` success state), it can be retried with an increased gas limit. uint80 gasLimit; } - /// @notice Contains the configuration for each DON ID - // @param configId (uint64(donId) << 32) | configVersion - mapping(uint64 configId => OracleSet oracleSet) internal s_configs; - - event ConfigSet(uint32 indexed donId, uint32 indexed configVersion, uint8 f, address[] signers); - /// @notice Emitted when a report is processed /// @param result The result of the attempted delivery. True if successful. event ReportProcessed( @@ -97,6 +81,12 @@ contract KeystoneForwarder is OwnerIsCreator, ITypeAndVersion, IRouter { bool result ); + /// @notice Contains the configuration for each DON ID + /// configId (uint64(donId) << 32) | configVersion + mapping(uint64 configId => OracleSet oracleSet) internal s_configs; + + event ConfigSet(uint32 indexed donId, uint32 indexed configVersion, uint8 f, address[] signers); + string public constant override typeAndVersion = "Forwarder and Router 1.0.0"; constructor() OwnerIsCreator() { @@ -180,8 +170,7 @@ contract KeystoneForwarder is OwnerIsCreator, ITypeAndVersion, IRouter { bytes32 workflowExecutionId, bytes2 reportId ) public pure returns (bytes32) { - // This is slightly cheaper compared to - // keccak256(abi.encode(receiver, workflowExecutionId, reportId)); + // This is slightly cheaper compared to `keccak256(abi.encode(receiver, workflowExecutionId, reportId));` return keccak256(bytes.concat(bytes20(uint160(receiver)), workflowExecutionId, reportId)); } diff --git a/contracts/src/v0.8/keystone/OCR3Capability.sol b/contracts/src/v0.8/keystone/OCR3Capability.sol index 22ab9394cc2..d054434361c 100644 --- a/contracts/src/v0.8/keystone/OCR3Capability.sol +++ b/contracts/src/v0.8/keystone/OCR3Capability.sol @@ -1,20 +1,21 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.24; -import {ConfirmedOwner} from "../shared/access/ConfirmedOwner.sol"; +import {OwnerIsCreator} from "../shared/access/OwnerIsCreator.sol"; import {OCR2Abstract} from "./ocr/OCR2Abstract.sol"; -// OCR2Base provides config management compatible with OCR3 -contract OCR3Capability is ConfirmedOwner, OCR2Abstract { +/// @notice OCR2Base provides config management compatible with OCR3 +contract OCR3Capability is OwnerIsCreator, OCR2Abstract { error InvalidConfig(string message); error ReportingUnsupported(); - constructor() ConfirmedOwner(msg.sender) {} + string public constant override typeAndVersion = "Keystone 1.0.0"; + // incremented each time a new config is posted. This count is incorporated // into the config digest, to prevent replay attacks. uint32 internal s_configCount; - uint32 internal s_latestConfigBlockNumber; // makes it easier for offchain systems - // to extract config from logs. + // makes it easier for offchain systems to extract config from logs. + uint32 internal s_latestConfigBlockNumber; // Storing these fields used on the hot path in a ConfigInfo variable reduces the // retrieval of all of them to a single SLOAD. If any further fields are @@ -26,10 +27,6 @@ contract OCR3Capability is ConfirmedOwner, OCR2Abstract { } ConfigInfo internal s_configInfo; - /* - * Config logic - */ - // Reverts transaction if config args are invalid modifier checkConfigValid(uint256 numSigners, uint256 numTransmitters, uint256 f) { if (numSigners > MAX_NUM_ORACLES) revert InvalidConfig("too many signers"); @@ -39,28 +36,14 @@ contract OCR3Capability is ConfirmedOwner, OCR2Abstract { _; } - /// @inheritdoc OCR2Abstract - function latestConfigDigestAndEpoch() - external - view - virtual - override - returns (bool scanLogs, bytes32 configDigest, uint32 epoch) - { - return (true, bytes32(0), uint32(0)); - } - - // signer = [ 1 byte type | 2 byte len | n byte value ]... - - /** - * @notice sets offchain reporting protocol configuration incl. participating oracles - * @param _signers addresses with which oracles sign the reports - * @param _transmitters addresses oracles use to transmit the reports - * @param _f number of faulty oracles the system can tolerate - * @param _onchainConfig encoded on-chain contract configuration - * @param _offchainConfigVersion version number for offchainEncoding schema - * @param _offchainConfig encoded off-chain oracle configuration - */ + /// @notice sets offchain reporting protocol configuration incl. participating oracles + /// @param _signers addresses with which oracles sign the reports + /// @param _transmitters addresses oracles use to transmit the reports + /// @param _f number of faulty oracles the system can tolerate + /// @param _onchainConfig encoded on-chain contract configuration + /// @param _offchainConfigVersion version number for offchainEncoding schema + /// @param _offchainConfig encoded off-chain oracle configuration + /// @dev signer = [ 1 byte type | 2 byte len | n byte value ]... function setConfig( bytes[] calldata _signers, address[] calldata _transmitters, @@ -70,19 +53,17 @@ contract OCR3Capability is ConfirmedOwner, OCR2Abstract { bytes memory _offchainConfig ) external override checkConfigValid(_signers.length, _transmitters.length, _f) onlyOwner { // Bounded by MAX_NUM_ORACLES in OCR2Abstract.sol - for (uint256 i = 0; i < _signers.length; i++) { + for (uint256 i = 0; i < _signers.length; ++i) { if (_transmitters[i] == address(0)) revert InvalidConfig("transmitter must not be empty"); // add new signers bytes calldata publicKeys = _signers[i]; - uint16 offset = 0; - uint16 len = uint16(publicKeys.length); + uint256 offset = 0; + uint256 publicKeysLength = uint16(publicKeys.length); // scan through public keys to validate encoded format - while (offset < len) { - // solhint-disable-next-line no-unused-vars - uint8 keyType = uint8(publicKeys[offset]); - uint16 keyLen = uint16(uint8(publicKeys[offset + 1])) + (uint16(uint8(publicKeys[offset + 2])) << 8); - // solhint-disable-next-line no-unused-vars - bytes calldata publicKey = publicKeys[offset + 3:offset + 3 + keyLen]; + while (offset < publicKeysLength) { + if (offset + 3 > publicKeysLength) revert InvalidConfig("invalid signer pubKey encoding"); + uint256 keyLen = uint256(uint8(publicKeys[offset + 1])) + (uint256(uint8(publicKeys[offset + 2])) << 8); + if (offset + 3 + keyLen > publicKeysLength) revert InvalidConfig("invalid signer pubKey encoding"); offset += 3 + keyLen; } } @@ -149,12 +130,10 @@ contract OCR3Capability is ConfirmedOwner, OCR2Abstract { return bytes32((prefix & prefixMask) | (h & ~prefixMask)); } - /** - * @notice information about current offchain reporting protocol configuration - * @return configCount ordinal number of current config, out of all configs applied to this contract so far - * @return blockNumber block at which this config was set - * @return configDigest domain-separation tag for current config (see __configDigestFromConfigData) - */ + /// @notice information about current offchain reporting protocol configuration + /// @return configCount ordinal number of current config, out of all configs applied to this contract so far + /// @return blockNumber block at which this config was set + /// @return configDigest domain-separation tag for current config (see __configDigestFromConfigData) function latestConfigDetails() external view @@ -164,10 +143,6 @@ contract OCR3Capability is ConfirmedOwner, OCR2Abstract { return (s_configCount, s_latestConfigBlockNumber, s_configInfo.latestConfigDigest); } - function typeAndVersion() external pure override returns (string memory) { - return "Keystone 1.0.0"; - } - function transmit( // NOTE: If these parameters are changed, expectedMsgDataLength and/or // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly @@ -179,4 +154,15 @@ contract OCR3Capability is ConfirmedOwner, OCR2Abstract { ) external pure override { revert ReportingUnsupported(); } + + /// @inheritdoc OCR2Abstract + function latestConfigDigestAndEpoch() + external + view + virtual + override + returns (bool scanLogs, bytes32 configDigest, uint32 epoch) + { + return (true, bytes32(0), uint32(0)); + } } diff --git a/contracts/src/v0.8/keystone/interfaces/ICapabilityConfiguration.sol b/contracts/src/v0.8/keystone/interfaces/ICapabilityConfiguration.sol index 702d55dba9d..6c144474684 100644 --- a/contracts/src/v0.8/keystone/interfaces/ICapabilityConfiguration.sol +++ b/contracts/src/v0.8/keystone/interfaces/ICapabilityConfiguration.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.24; +pragma solidity ^0.8.0; /// @notice Interface for capability configuration contract. It MUST be /// implemented for a contract to be used as a capability configuration. diff --git a/contracts/src/v0.8/keystone/interfaces/IReceiver.sol b/contracts/src/v0.8/keystone/interfaces/IReceiver.sol index debe58feea4..9afa1d340a3 100644 --- a/contracts/src/v0.8/keystone/interfaces/IReceiver.sol +++ b/contracts/src/v0.8/keystone/interfaces/IReceiver.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.24; +pragma solidity ^0.8.0; /// @title IReceiver - receives keystone reports interface IReceiver { diff --git a/contracts/src/v0.8/keystone/interfaces/IRouter.sol b/contracts/src/v0.8/keystone/interfaces/IRouter.sol index 3209ae58311..3d2e24f550a 100644 --- a/contracts/src/v0.8/keystone/interfaces/IRouter.sol +++ b/contracts/src/v0.8/keystone/interfaces/IRouter.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.24; +pragma solidity ^0.8.4; /// @title IRouter - delivers keystone reports to receiver interface IRouter { diff --git a/contracts/src/v0.8/keystone/lib/KeystoneFeedDefaultMetadataLib.sol b/contracts/src/v0.8/keystone/lib/KeystoneFeedDefaultMetadataLib.sol index 061789be5c2..2eb9b5529de 100644 --- a/contracts/src/v0.8/keystone/lib/KeystoneFeedDefaultMetadataLib.sol +++ b/contracts/src/v0.8/keystone/lib/KeystoneFeedDefaultMetadataLib.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.24; +pragma solidity ^0.8.0; library KeystoneFeedDefaultMetadataLib { /** diff --git a/contracts/src/v0.8/keystone/ocr/OCR2Abstract.sol b/contracts/src/v0.8/keystone/ocr/OCR2Abstract.sol index 4eb5cb069b7..af822a69854 100644 --- a/contracts/src/v0.8/keystone/ocr/OCR2Abstract.sol +++ b/contracts/src/v0.8/keystone/ocr/OCR2Abstract.sol @@ -62,8 +62,8 @@ abstract contract OCR2Abstract is ITypeAndVersion { returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest); /** - * @notice optionally emited to indicate the latest configDigest and epoch for - which a report was successfully transmited. Alternatively, the contract may + * @notice optionally emitted to indicate the latest configDigest and epoch for + which a report was successfully transmitted. Alternatively, the contract may use latestConfigDigestAndEpoch with scanLogs set to false. */ event Transmitted(bytes32 configDigest, uint32 epoch); diff --git a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_UpdateNodesTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_UpdateNodesTest.t.sol index fe846536270..218161f3b2c 100644 --- a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_UpdateNodesTest.t.sol +++ b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_UpdateNodesTest.t.sol @@ -138,6 +138,25 @@ contract CapabilitiesRegistry_UpdateNodesTest is BaseTest { s_CapabilitiesRegistry.updateNodes(nodes); } + function test_RevertWhen_EncryptionPublicKeyEmpty() public { + changePrank(NODE_OPERATOR_ONE_ADMIN); + CapabilitiesRegistry.NodeParams[] memory nodes = new CapabilitiesRegistry.NodeParams[](1); + + bytes32[] memory hashedCapabilityIds = new bytes32[](1); + hashedCapabilityIds[0] = s_basicHashedCapabilityId; + + nodes[0] = CapabilitiesRegistry.NodeParams({ + nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, + p2pId: P2P_ID, + signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, + encryptionPublicKey: bytes32(""), + hashedCapabilityIds: hashedCapabilityIds + }); + + vm.expectRevert(abi.encodeWithSelector(CapabilitiesRegistry.InvalidNodeEncryptionPublicKey.selector, bytes32(""))); + s_CapabilitiesRegistry.updateNodes(nodes); + } + function test_RevertWhen_NodeSignerAlreadyAssignedToAnotherNode() public { changePrank(NODE_OPERATOR_ONE_ADMIN); CapabilitiesRegistry.NodeParams[] memory nodes = new CapabilitiesRegistry.NodeParams[](1); diff --git a/contracts/src/v0.8/keystone/test/KeystoneForwarderBaseTest.t.sol b/contracts/src/v0.8/keystone/test/KeystoneForwarderBaseTest.t.sol index c106c2b2b21..0dd480a4282 100644 --- a/contracts/src/v0.8/keystone/test/KeystoneForwarderBaseTest.t.sol +++ b/contracts/src/v0.8/keystone/test/KeystoneForwarderBaseTest.t.sol @@ -32,7 +32,7 @@ contract BaseTest is Test { uint256 seed = 0; - for (uint256 i; i < MAX_ORACLES; i++) { + for (uint256 i; i < MAX_ORACLES; ++i) { uint256 mockPK = seed + i + 1; s_signers[i].mockPrivateKey = mockPK; s_signers[i].signerAddress = vm.addr(mockPK); @@ -41,7 +41,7 @@ contract BaseTest is Test { function _getSignerAddresses() internal view returns (address[] memory) { address[] memory signerAddrs = new address[](s_signers.length); - for (uint256 i = 0; i < signerAddrs.length; i++) { + for (uint256 i = 0; i < signerAddrs.length; ++i) { signerAddrs[i] = s_signers[i].signerAddress; } return signerAddrs; @@ -49,7 +49,7 @@ contract BaseTest is Test { function _getSignerAddresses(uint256 limit) internal view returns (address[] memory) { address[] memory signerAddrs = new address[](limit); - for (uint256 i = 0; i < limit; i++) { + for (uint256 i = 0; i < limit; ++i) { signerAddrs[i] = s_signers[i].signerAddress; } return signerAddrs; @@ -61,7 +61,7 @@ contract BaseTest is Test { uint256 requiredSignatures ) internal view returns (bytes[] memory signatures) { signatures = new bytes[](requiredSignatures); - for (uint256 i = 0; i < requiredSignatures; i++) { + for (uint256 i = 0; i < requiredSignatures; ++i) { (uint8 v, bytes32 r, bytes32 s) = vm.sign( s_signers[i].mockPrivateKey, keccak256(abi.encodePacked(keccak256(report), reportContext)) diff --git a/contracts/src/v0.8/keystone/test/mocks/MaliciousReportReceiver.sol b/contracts/src/v0.8/keystone/test/mocks/MaliciousReportReceiver.sol index ac203cccf67..8f039b5f0ce 100644 --- a/contracts/src/v0.8/keystone/test/mocks/MaliciousReportReceiver.sol +++ b/contracts/src/v0.8/keystone/test/mocks/MaliciousReportReceiver.sol @@ -10,7 +10,7 @@ contract MaliciousReportReceiver is IReceiver, IERC165 { function onReport(bytes calldata metadata, bytes calldata rawReport) external { // Exhaust all gas that was provided - for (uint256 i = 0; i < 1_000_000_000; i++) { + for (uint256 i = 0; i < 1_000_000_000; ++i) { bytes[] memory mercuryReports = abi.decode(rawReport, (bytes[])); latestReport = rawReport; emit MessageReceived(metadata, mercuryReports); diff --git a/contracts/src/v0.8/keystone/test/mocks/MaliciousRevertingReceiver.sol b/contracts/src/v0.8/keystone/test/mocks/MaliciousRevertingReceiver.sol index d128b4a751e..f45e95afb2c 100644 --- a/contracts/src/v0.8/keystone/test/mocks/MaliciousRevertingReceiver.sol +++ b/contracts/src/v0.8/keystone/test/mocks/MaliciousRevertingReceiver.sol @@ -10,12 +10,12 @@ contract MaliciousRevertingReceiver is IReceiver, IERC165 { function onReport(bytes calldata, bytes calldata) external view override { // consumes about 63/64 of all gas available uint256 targetGasRemaining = 200; - for (uint256 i = 0; gasleft() > targetGasRemaining; i++) {} + for (uint256 i = 0; gasleft() > targetGasRemaining; ++i) {} } function supportsInterface(bytes4 interfaceId) public pure override returns (bool) { // Consume up to the maximum amount of gas that can be consumed in this check - for (uint256 i = 0; i < 500; i++) {} + for (uint256 i = 0; i < 500; ++i) {} return interfaceId == type(IReceiver).interfaceId || interfaceId == type(IERC165).interfaceId; } diff --git a/core/capabilities/aggregator_factory.go b/core/capabilities/aggregator_factory.go index 18336e3f802..1abf58b6c12 100644 --- a/core/capabilities/aggregator_factory.go +++ b/core/capabilities/aggregator_factory.go @@ -3,6 +3,7 @@ package capabilities import ( "fmt" + "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/aggregators" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/datafeeds" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/types" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -14,7 +15,9 @@ func NewAggregator(name string, config values.Map, lggr logger.Logger) (types.Ag switch name { case "data_feeds": mc := streams.NewCodec(lggr) - return datafeeds.NewDataFeedsAggregator(config, mc, lggr) + return datafeeds.NewDataFeedsAggregator(config, mc) + case "identical": + return aggregators.NewIdenticalAggregator(config) default: return nil, fmt.Errorf("aggregator %s not supported", name) } diff --git a/core/capabilities/ccip/ccip_integration_tests/ccipreader/ccipreader_test.go b/core/capabilities/ccip/ccip_integration_tests/ccipreader/ccipreader_test.go deleted file mode 100644 index f80f7043a39..00000000000 --- a/core/capabilities/ccip/ccip_integration_tests/ccipreader/ccipreader_test.go +++ /dev/null @@ -1,568 +0,0 @@ -package ccipreader - -import ( - "context" - "math/big" - "sort" - "testing" - "time" - - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/crypto" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.uber.org/zap/zapcore" - "golang.org/x/exp/maps" - - "github.com/smartcontractkit/chainlink-common/pkg/types" - cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_reader_tester" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - evmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" - - readermocks "github.com/smartcontractkit/chainlink-ccip/mocks/pkg/contractreader" - "github.com/smartcontractkit/chainlink-ccip/pkg/consts" - "github.com/smartcontractkit/chainlink-ccip/pkg/contractreader" - ccipreaderpkg "github.com/smartcontractkit/chainlink-ccip/pkg/reader" - "github.com/smartcontractkit/chainlink-ccip/plugintypes" -) - -const ( - chainS1 = cciptypes.ChainSelector(1) - chainS2 = cciptypes.ChainSelector(2) - chainS3 = cciptypes.ChainSelector(3) - chainD = cciptypes.ChainSelector(4) -) - -func TestCCIPReader_CommitReportsGTETimestamp(t *testing.T) { - ctx := testutils.Context(t) - - cfg := evmtypes.ChainReaderConfig{ - Contracts: map[string]evmtypes.ChainContractReader{ - consts.ContractNameOffRamp: { - ContractPollingFilter: evmtypes.ContractPollingFilter{ - GenericEventNames: []string{consts.EventNameCommitReportAccepted}, - }, - ContractABI: ccip_reader_tester.CCIPReaderTesterABI, - Configs: map[string]*evmtypes.ChainReaderDefinition{ - consts.EventNameCommitReportAccepted: { - ChainSpecificName: consts.EventNameCommitReportAccepted, - ReadType: evmtypes.Event, - }, - }, - }, - }, - } - - onRampAddress := utils.RandomAddress() - s := testSetup(ctx, t, chainD, chainD, nil, cfg, map[cciptypes.ChainSelector][]types.BoundContract{ - chainS1: { - { - Address: onRampAddress.Hex(), - Name: consts.ContractNameOnRamp, - }, - }, - }) - - tokenA := common.HexToAddress("123") - const numReports = 5 - - for i := 0; i < numReports; i++ { - _, err := s.contract.EmitCommitReportAccepted(s.auth, ccip_reader_tester.OffRampCommitReport{ - PriceUpdates: ccip_reader_tester.InternalPriceUpdates{ - TokenPriceUpdates: []ccip_reader_tester.InternalTokenPriceUpdate{ - { - SourceToken: tokenA, - UsdPerToken: big.NewInt(1000), - }, - }, - GasPriceUpdates: []ccip_reader_tester.InternalGasPriceUpdate{ - { - DestChainSelector: uint64(chainD), - UsdPerUnitGas: big.NewInt(90), - }, - }, - }, - MerkleRoots: []ccip_reader_tester.InternalMerkleRoot{ - { - SourceChainSelector: uint64(chainS1), - MinSeqNr: 10, - MaxSeqNr: 20, - MerkleRoot: [32]byte{uint8(i) + 1}, //nolint:gosec // this won't overflow - OnRampAddress: common.LeftPadBytes(onRampAddress.Bytes(), 32), - }, - }, - RmnSignatures: []ccip_reader_tester.IRMNRemoteSignature{ - { - R: [32]byte{1}, - S: [32]byte{2}, - }, - { - R: [32]byte{3}, - S: [32]byte{4}, - }, - }, - RmnRawVs: big.NewInt(100), - }) - assert.NoError(t, err) - s.sb.Commit() - } - - // Need to replay as sometimes the logs are not picked up by the log poller (?) - // Maybe another situation where chain reader doesn't register filters as expected. - require.NoError(t, s.lp.Replay(ctx, 1)) - - var reports []plugintypes.CommitPluginReportWithMeta - var err error - require.Eventually(t, func() bool { - reports, err = s.reader.CommitReportsGTETimestamp( - ctx, - chainD, - time.Unix(30, 0), // Skips first report, simulated backend report timestamps are [20, 30, 40, ...] - 10, - ) - require.NoError(t, err) - return len(reports) == numReports-1 - }, tests.WaitTimeout(t), 50*time.Millisecond) - - assert.Len(t, reports, numReports-1) - assert.Len(t, reports[0].Report.MerkleRoots, 1) - assert.Equal(t, chainS1, reports[0].Report.MerkleRoots[0].ChainSel) - assert.Equal(t, onRampAddress.Bytes(), []byte(reports[0].Report.MerkleRoots[0].OnRampAddress)) - assert.Equal(t, cciptypes.SeqNum(10), reports[0].Report.MerkleRoots[0].SeqNumsRange.Start()) - assert.Equal(t, cciptypes.SeqNum(20), reports[0].Report.MerkleRoots[0].SeqNumsRange.End()) - assert.Equal(t, "0x0200000000000000000000000000000000000000000000000000000000000000", - reports[0].Report.MerkleRoots[0].MerkleRoot.String()) - - assert.Equal(t, tokenA.String(), string(reports[0].Report.PriceUpdates.TokenPriceUpdates[0].TokenID)) - assert.Equal(t, uint64(1000), reports[0].Report.PriceUpdates.TokenPriceUpdates[0].Price.Uint64()) - - assert.Equal(t, chainD, reports[0].Report.PriceUpdates.GasPriceUpdates[0].ChainSel) - assert.Equal(t, uint64(90), reports[0].Report.PriceUpdates.GasPriceUpdates[0].GasPrice.Uint64()) - - // TODO assert once chainlink-ccip changes are done - // assert.Len(t, reports[0].Report.RMNSignatures, 2) - // assert.Equal(t, reports[0].Report.RMNSignatures[0].R, [32]byte{1}) - // assert.Equal(t, reports[0].Report.RMNSignatures[0].S, [32]byte{2}) -} - -func TestCCIPReader_ExecutedMessageRanges(t *testing.T) { - ctx := testutils.Context(t) - cfg := evmtypes.ChainReaderConfig{ - Contracts: map[string]evmtypes.ChainContractReader{ - consts.ContractNameOffRamp: { - ContractPollingFilter: evmtypes.ContractPollingFilter{ - GenericEventNames: []string{consts.EventNameExecutionStateChanged}, - }, - ContractABI: ccip_reader_tester.CCIPReaderTesterABI, - Configs: map[string]*evmtypes.ChainReaderDefinition{ - consts.EventNameExecutionStateChanged: { - ChainSpecificName: consts.EventNameExecutionStateChanged, - ReadType: evmtypes.Event, - }, - }, - }, - }, - } - - s := testSetup(ctx, t, chainD, chainD, nil, cfg, nil) - - _, err := s.contract.EmitExecutionStateChanged( - s.auth, - uint64(chainS1), - 14, - cciptypes.Bytes32{1, 0, 0, 1}, - 1, - []byte{1, 2, 3, 4}, - ) - assert.NoError(t, err) - s.sb.Commit() - - _, err = s.contract.EmitExecutionStateChanged( - s.auth, - uint64(chainS1), - 15, - cciptypes.Bytes32{1, 0, 0, 2}, - 1, - []byte{1, 2, 3, 4, 5}, - ) - assert.NoError(t, err) - s.sb.Commit() - - // Need to replay as sometimes the logs are not picked up by the log poller (?) - // Maybe another situation where chain reader doesn't register filters as expected. - require.NoError(t, s.lp.Replay(ctx, 1)) - - var executedRanges []cciptypes.SeqNumRange - require.Eventually(t, func() bool { - executedRanges, err = s.reader.ExecutedMessageRanges( - ctx, - chainS1, - chainD, - cciptypes.NewSeqNumRange(14, 15), - ) - require.NoError(t, err) - return len(executedRanges) == 2 - }, testutils.WaitTimeout(t), 50*time.Millisecond) - - assert.Equal(t, cciptypes.SeqNum(14), executedRanges[0].Start()) - assert.Equal(t, cciptypes.SeqNum(14), executedRanges[0].End()) - - assert.Equal(t, cciptypes.SeqNum(15), executedRanges[1].Start()) - assert.Equal(t, cciptypes.SeqNum(15), executedRanges[1].End()) -} - -func TestCCIPReader_MsgsBetweenSeqNums(t *testing.T) { - ctx := testutils.Context(t) - - cfg := evmtypes.ChainReaderConfig{ - Contracts: map[string]evmtypes.ChainContractReader{ - consts.ContractNameOnRamp: { - ContractPollingFilter: evmtypes.ContractPollingFilter{ - GenericEventNames: []string{consts.EventNameCCIPMessageSent}, - }, - ContractABI: ccip_reader_tester.CCIPReaderTesterABI, - Configs: map[string]*evmtypes.ChainReaderDefinition{ - consts.EventNameCCIPMessageSent: { - ChainSpecificName: "CCIPMessageSent", - ReadType: evmtypes.Event, - }, - }, - }, - }, - } - - s := testSetup(ctx, t, chainS1, chainD, nil, cfg, nil) - - _, err := s.contract.EmitCCIPMessageSent(s.auth, uint64(chainD), ccip_reader_tester.InternalEVM2AnyRampMessage{ - Header: ccip_reader_tester.InternalRampMessageHeader{ - MessageId: [32]byte{1, 0, 0, 0, 0}, - SourceChainSelector: uint64(chainS1), - DestChainSelector: uint64(chainD), - SequenceNumber: 10, - }, - Sender: utils.RandomAddress(), - Data: make([]byte, 0), - Receiver: utils.RandomAddress().Bytes(), - ExtraArgs: make([]byte, 0), - FeeToken: utils.RandomAddress(), - FeeTokenAmount: big.NewInt(0), - FeeValueJuels: big.NewInt(0), - TokenAmounts: make([]ccip_reader_tester.InternalEVM2AnyTokenTransfer, 0), - }) - assert.NoError(t, err) - - _, err = s.contract.EmitCCIPMessageSent(s.auth, uint64(chainD), ccip_reader_tester.InternalEVM2AnyRampMessage{ - Header: ccip_reader_tester.InternalRampMessageHeader{ - MessageId: [32]byte{1, 0, 0, 0, 1}, - SourceChainSelector: uint64(chainS1), - DestChainSelector: uint64(chainD), - SequenceNumber: 15, - }, - Sender: utils.RandomAddress(), - Data: make([]byte, 0), - Receiver: utils.RandomAddress().Bytes(), - ExtraArgs: make([]byte, 0), - FeeToken: utils.RandomAddress(), - FeeTokenAmount: big.NewInt(0), - FeeValueJuels: big.NewInt(0), - TokenAmounts: make([]ccip_reader_tester.InternalEVM2AnyTokenTransfer, 0), - }) - assert.NoError(t, err) - - s.sb.Commit() - - // Need to replay as sometimes the logs are not picked up by the log poller (?) - // Maybe another situation where chain reader doesn't register filters as expected. - require.NoError(t, s.lp.Replay(ctx, 1)) - - var msgs []cciptypes.Message - require.Eventually(t, func() bool { - msgs, err = s.reader.MsgsBetweenSeqNums( - ctx, - chainS1, - cciptypes.NewSeqNumRange(5, 20), - ) - require.NoError(t, err) - return len(msgs) == 2 - }, tests.WaitTimeout(t), 100*time.Millisecond) - - require.Len(t, msgs, 2) - // sort to ensure ascending order of sequence numbers. - sort.Slice(msgs, func(i, j int) bool { - return msgs[i].Header.SequenceNumber < msgs[j].Header.SequenceNumber - }) - require.Equal(t, cciptypes.SeqNum(10), msgs[0].Header.SequenceNumber) - require.Equal(t, cciptypes.SeqNum(15), msgs[1].Header.SequenceNumber) - for _, msg := range msgs { - require.Equal(t, chainS1, msg.Header.SourceChainSelector) - require.Equal(t, chainD, msg.Header.DestChainSelector) - } -} - -func TestCCIPReader_NextSeqNum(t *testing.T) { - ctx := testutils.Context(t) - - onChainSeqNums := map[cciptypes.ChainSelector]cciptypes.SeqNum{ - chainS1: 10, - chainS2: 20, - chainS3: 30, - } - - cfg := evmtypes.ChainReaderConfig{ - Contracts: map[string]evmtypes.ChainContractReader{ - consts.ContractNameOffRamp: { - ContractABI: ccip_reader_tester.CCIPReaderTesterABI, - Configs: map[string]*evmtypes.ChainReaderDefinition{ - consts.MethodNameGetSourceChainConfig: { - ChainSpecificName: "getSourceChainConfig", - ReadType: evmtypes.Method, - }, - }, - }, - }, - } - - s := testSetup(ctx, t, chainD, chainD, onChainSeqNums, cfg, nil) - - seqNums, err := s.reader.NextSeqNum(ctx, []cciptypes.ChainSelector{chainS1, chainS2, chainS3}) - assert.NoError(t, err) - assert.Len(t, seqNums, 3) - assert.Equal(t, cciptypes.SeqNum(10), seqNums[0]) - assert.Equal(t, cciptypes.SeqNum(20), seqNums[1]) - assert.Equal(t, cciptypes.SeqNum(30), seqNums[2]) -} - -func TestCCIPReader_GetExpectedNextSequenceNumber(t *testing.T) { - ctx := testutils.Context(t) - - cfg := evmtypes.ChainReaderConfig{ - Contracts: map[string]evmtypes.ChainContractReader{ - consts.ContractNameOnRamp: { - ContractABI: ccip_reader_tester.CCIPReaderTesterABI, - Configs: map[string]*evmtypes.ChainReaderDefinition{ - consts.MethodNameGetExpectedNextSequenceNumber: { - ChainSpecificName: "getExpectedNextSequenceNumber", - ReadType: evmtypes.Method, - }, - }, - }, - }, - } - - s := testSetup(ctx, t, chainS1, chainD, nil, cfg, nil) - - _, err := s.contract.SetDestChainSeqNr(s.auth, uint64(chainD), 10) - require.NoError(t, err) - s.sb.Commit() - - seqNum, err := s.reader.GetExpectedNextSequenceNumber(ctx, chainS1, chainD) - require.NoError(t, err) - require.Equal(t, cciptypes.SeqNum(10)+1, seqNum) - - _, err = s.contract.SetDestChainSeqNr(s.auth, uint64(chainD), 25) - require.NoError(t, err) - s.sb.Commit() - - seqNum, err = s.reader.GetExpectedNextSequenceNumber(ctx, chainS1, chainD) - require.NoError(t, err) - require.Equal(t, cciptypes.SeqNum(25)+1, seqNum) -} - -func TestCCIPReader_Nonces(t *testing.T) { - ctx := testutils.Context(t) - var nonces = map[cciptypes.ChainSelector]map[common.Address]uint64{ - chainS1: { - utils.RandomAddress(): 10, - utils.RandomAddress(): 20, - }, - chainS2: { - utils.RandomAddress(): 30, - utils.RandomAddress(): 40, - }, - chainS3: { - utils.RandomAddress(): 50, - utils.RandomAddress(): 60, - }, - } - - cfg := evmtypes.ChainReaderConfig{ - Contracts: map[string]evmtypes.ChainContractReader{ - consts.ContractNameNonceManager: { - ContractABI: ccip_reader_tester.CCIPReaderTesterABI, - Configs: map[string]*evmtypes.ChainReaderDefinition{ - consts.MethodNameGetInboundNonce: { - ChainSpecificName: "getInboundNonce", - ReadType: evmtypes.Method, - }, - }, - }, - }, - } - - s := testSetup(ctx, t, chainD, chainD, nil, cfg, nil) - - // Add some nonces. - for chain, addrs := range nonces { - for addr, nonce := range addrs { - _, err := s.contract.SetInboundNonce(s.auth, uint64(chain), nonce, addr.Bytes()) - assert.NoError(t, err) - } - } - s.sb.Commit() - - for sourceChain, addrs := range nonces { - var addrQuery []string - for addr := range addrs { - addrQuery = append(addrQuery, addr.String()) - } - addrQuery = append(addrQuery, utils.RandomAddress().String()) - - results, err := s.reader.Nonces(ctx, sourceChain, chainD, addrQuery) - assert.NoError(t, err) - assert.Len(t, results, len(addrQuery)) - for addr, nonce := range addrs { - assert.Equal(t, nonce, results[addr.String()]) - } - } -} - -func testSetup( - ctx context.Context, - t *testing.T, - readerChain, - destChain cciptypes.ChainSelector, - onChainSeqNums map[cciptypes.ChainSelector]cciptypes.SeqNum, - cfg evmtypes.ChainReaderConfig, - otherBindings map[cciptypes.ChainSelector][]types.BoundContract, -) *testSetupData { - const chainID = 1337 - - // Generate a new key pair for the simulated account - privateKey, err := crypto.GenerateKey() - assert.NoError(t, err) - // Set up the genesis account with balance - blnc, ok := big.NewInt(0).SetString("999999999999999999999999999999999999", 10) - assert.True(t, ok) - alloc := map[common.Address]core.GenesisAccount{crypto.PubkeyToAddress(privateKey.PublicKey): {Balance: blnc}} - simulatedBackend := backends.NewSimulatedBackend(alloc, 0) - // Create a transactor - - auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(chainID)) - assert.NoError(t, err) - auth.GasLimit = uint64(0) - - // Deploy the contract - address, _, _, err := ccip_reader_tester.DeployCCIPReaderTester(auth, simulatedBackend) - assert.NoError(t, err) - simulatedBackend.Commit() - - // Setup contract client - contract, err := ccip_reader_tester.NewCCIPReaderTester(address, simulatedBackend) - assert.NoError(t, err) - - lggr := logger.TestLogger(t) - lggr.SetLogLevel(zapcore.ErrorLevel) - db := pgtest.NewSqlxDB(t) - lpOpts := logpoller.Opts{ - PollPeriod: time.Millisecond, - FinalityDepth: 0, - BackfillBatchSize: 10, - RpcBatchSize: 10, - KeepFinalizedBlocksDepth: 100000, - } - cl := client.NewSimulatedBackendClient(t, simulatedBackend, big.NewInt(0).SetUint64(uint64(readerChain))) - headTracker := headtracker.NewSimulatedHeadTracker(cl, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) - lp := logpoller.NewLogPoller(logpoller.NewORM(big.NewInt(0).SetUint64(uint64(readerChain)), db, lggr), - cl, - lggr, - headTracker, - lpOpts, - ) - assert.NoError(t, lp.Start(ctx)) - - for sourceChain, seqNum := range onChainSeqNums { - _, err1 := contract.SetSourceChainConfig(auth, uint64(sourceChain), ccip_reader_tester.OffRampSourceChainConfig{ - IsEnabled: true, - MinSeqNr: uint64(seqNum), - OnRamp: utils.RandomAddress().Bytes(), - }) - assert.NoError(t, err1) - simulatedBackend.Commit() - scc, err1 := contract.GetSourceChainConfig(&bind.CallOpts{Context: ctx}, uint64(sourceChain)) - assert.NoError(t, err1) - assert.Equal(t, seqNum, cciptypes.SeqNum(scc.MinSeqNr)) - } - - contractNames := maps.Keys(cfg.Contracts) - assert.Len(t, contractNames, 1, "test setup assumes there is only one contract") - - cr, err := evm.NewChainReaderService(ctx, lggr, lp, headTracker, cl, cfg) - require.NoError(t, err) - - extendedCr := contractreader.NewExtendedContractReader(cr) - err = extendedCr.Bind(ctx, []types.BoundContract{ - { - Address: address.String(), - Name: contractNames[0], - }, - }) - require.NoError(t, err) - var otherCrs = make(map[cciptypes.ChainSelector]contractreader.Extended) - for chain, bindings := range otherBindings { - m := readermocks.NewMockContractReaderFacade(t) - m.EXPECT().Bind(ctx, bindings).Return(nil) - ecr := contractreader.NewExtendedContractReader(m) - err = ecr.Bind(ctx, bindings) - require.NoError(t, err) - otherCrs[chain] = ecr - } - - err = cr.Start(ctx) - require.NoError(t, err) - - contractReaders := map[cciptypes.ChainSelector]contractreader.Extended{readerChain: extendedCr} - for chain, cr := range otherCrs { - contractReaders[chain] = cr - } - contractWriters := make(map[cciptypes.ChainSelector]types.ChainWriter) - reader := ccipreaderpkg.NewCCIPReaderWithExtendedContractReaders(ctx, lggr, contractReaders, contractWriters, destChain, nil) - - t.Cleanup(func() { - require.NoError(t, cr.Close()) - require.NoError(t, lp.Close()) - require.NoError(t, db.Close()) - }) - - return &testSetupData{ - contractAddr: address, - contract: contract, - sb: simulatedBackend, - auth: auth, - lp: lp, - cl: cl, - reader: reader, - } -} - -type testSetupData struct { - contractAddr common.Address - contract *ccip_reader_tester.CCIPReaderTester - sb *backends.SimulatedBackend - auth *bind.TransactOpts - lp logpoller.LogPoller - cl client.Client - reader ccipreaderpkg.CCIPReader -} diff --git a/core/capabilities/ccip/ccipevm/executecodec.go b/core/capabilities/ccip/ccipevm/executecodec.go index 7ce380b8a02..93ec7b1e64b 100644 --- a/core/capabilities/ccip/ccipevm/executecodec.go +++ b/core/capabilities/ccip/ccipevm/executecodec.go @@ -37,7 +37,7 @@ func NewExecutePluginCodecV1() *ExecutePluginCodecV1 { } func (e *ExecutePluginCodecV1) Encode(ctx context.Context, report cciptypes.ExecutePluginReport) ([]byte, error) { - evmReport := make([]offramp.InternalExecutionReportSingleChain, 0, len(report.ChainReports)) + evmReport := make([]offramp.InternalExecutionReport, 0, len(report.ChainReports)) for _, chainReport := range report.ChainReports { if chainReport.ProofFlagBits.IsEmpty() { @@ -94,7 +94,7 @@ func (e *ExecutePluginCodecV1) Encode(ctx context.Context, report cciptypes.Exec }) } - evmChainReport := offramp.InternalExecutionReportSingleChain{ + evmChainReport := offramp.InternalExecutionReport{ SourceChainSelector: uint64(chainReport.SourceChainSelector), Messages: evmMessages, OffchainTokenData: chainReport.OffchainTokenData, @@ -116,8 +116,8 @@ func (e *ExecutePluginCodecV1) Decode(ctx context.Context, encodedReport []byte) return cciptypes.ExecutePluginReport{}, fmt.Errorf("unpacked report is empty") } - evmReportRaw := abi.ConvertType(unpacked[0], new([]offramp.InternalExecutionReportSingleChain)) - evmReportPtr, is := evmReportRaw.(*[]offramp.InternalExecutionReportSingleChain) + evmReportRaw := abi.ConvertType(unpacked[0], new([]offramp.InternalExecutionReport)) + evmReportPtr, is := evmReportRaw.(*[]offramp.InternalExecutionReport) if !is { return cciptypes.ExecutePluginReport{}, fmt.Errorf("got an unexpected report type %T", unpacked[0]) } diff --git a/core/capabilities/ccip/configs/evm/contract_reader.go b/core/capabilities/ccip/configs/evm/contract_reader.go index 5326c5efd87..f95030afc3e 100644 --- a/core/capabilities/ccip/configs/evm/contract_reader.go +++ b/core/capabilities/ccip/configs/evm/contract_reader.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_home" "github.com/smartcontractkit/chainlink-ccip/pkg/consts" @@ -29,13 +30,10 @@ var ( nonceManagerABI = evmtypes.MustGetABI(nonce_manager.NonceManagerABI) priceFeedABI = evmtypes.MustGetABI(aggregator_v3_interface.AggregatorV3InterfaceABI) rmnRemoteABI = evmtypes.MustGetABI(rmn_remote.RMNRemoteABI) - rmnHomeABI = evmtypes.MustGetABI(rmnHomeString) + rmnHomeABI = evmtypes.MustGetABI(rmn_home.RMNHomeABI) routerABI = evmtypes.MustGetABI(router.RouterABI) ) -// TODO: replace with generated ABI when the contract will be defined -var rmnHomeString = "[{\"inputs\":[],\"name\":\"getAllConfigs\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"num\",\"type\":\"uint256\"}],\"name\":\"store\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" - func MergeReaderConfigs(configs ...evmrelaytypes.ChainReaderConfig) evmrelaytypes.ChainReaderConfig { allContracts := make(map[string]evmrelaytypes.ChainContractReader) for _, c := range configs { @@ -83,6 +81,10 @@ var DestReaderConfig = evmrelaytypes.ChainReaderConfig{ ChainSpecificName: mustGetMethodName("getSourceChainConfig", offrampABI), ReadType: evmrelaytypes.Method, }, + "OffRampGetAllSourceChainConfigs": { + ChainSpecificName: mustGetMethodName("getAllSourceChainConfigs", offrampABI), + ReadType: evmrelaytypes.Method, + }, consts.EventNameCommitReportAccepted: { ChainSpecificName: mustGetEventName(consts.EventNameCommitReportAccepted, offrampABI), ReadType: evmrelaytypes.Event, @@ -160,11 +162,10 @@ var DestReaderConfig = evmrelaytypes.ChainReaderConfig{ ChainSpecificName: mustGetMethodName("getVersionedConfig", rmnRemoteABI), ReadType: evmrelaytypes.Method, }, - // TODO: to uncomment when the latest version of the contract will be merged. - // consts.MethodNameGetReportDigestHeader: { - // ChainSpecificName: mustGetMethodName("getReportDigestHeader", rmnRemoteABI), - // ReadType: evmrelaytypes.Method, - // }, + consts.MethodNameGetReportDigestHeader: { + ChainSpecificName: mustGetMethodName("getReportDigestHeader", rmnRemoteABI), + ReadType: evmrelaytypes.Method, + }, }, }, consts.ContractNameRouter: { @@ -306,7 +307,7 @@ var HomeChainReaderConfigRaw = evmrelaytypes.ChainReaderConfig{ }, }, consts.ContractNameRMNHome: { - ContractABI: rmnHomeString, + ContractABI: rmn_home.RMNHomeABI, Configs: map[string]*evmrelaytypes.ChainReaderDefinition{ consts.MethodNameGetAllConfigs: { ChainSpecificName: mustGetMethodName("getAllConfigs", rmnHomeABI), diff --git a/core/capabilities/ccip/oraclecreator/plugin.go b/core/capabilities/ccip/oraclecreator/plugin.go index a7205ad3d3a..1eb3bec36fe 100644 --- a/core/capabilities/ccip/oraclecreator/plugin.go +++ b/core/capabilities/ccip/oraclecreator/plugin.go @@ -31,6 +31,7 @@ import ( cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" commitocr3 "github.com/smartcontractkit/chainlink-ccip/commit" + "github.com/smartcontractkit/chainlink-ccip/commit/merkleroot/rmn" execocr3 "github.com/smartcontractkit/chainlink-ccip/execute" "github.com/smartcontractkit/chainlink-ccip/pkg/consts" ccipreaderpkg "github.com/smartcontractkit/chainlink-ccip/pkg/reader" @@ -226,6 +227,14 @@ func (i *pluginOracleCreator) createFactoryAndTransmitter( var factory ocr3types.ReportingPluginFactory[[]byte] var transmitter ocr3types.ContractTransmitter[[]byte] if config.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) { + if !i.peerWrapper.IsStarted() { + return nil, nil, fmt.Errorf("peer wrapper is not started") + } + + rmnPeerClient := rmn.NewPeerClient(i.lggr, i.peerWrapper.PeerGroupFactory, i.bootstrapperLocators) + + rmnCrypto := ccipevm.NewEVMRMNCrypto() + factory = commitocr3.NewPluginFactory( i.lggr. Named("CCIPCommitPlugin"). @@ -240,6 +249,8 @@ func (i *pluginOracleCreator) createFactoryAndTransmitter( i.homeChainSelector, contractReaders, chainWriters, + rmnPeerClient, + rmnCrypto, ) transmitter = ocrimpls.NewCommitContractTransmitter[[]byte](destChainWriter, ocrtypes.Account(destFromAccounts[0]), diff --git a/core/capabilities/remote/types/messages.pb.go b/core/capabilities/remote/types/messages.pb.go index f5b77b5c15a..0675bcc0f2a 100644 --- a/core/capabilities/remote/types/messages.pb.go +++ b/core/capabilities/remote/types/messages.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.34.2 +// protoc-gen-go v1.35.1 // protoc v4.25.1 // source: core/capabilities/remote/types/messages.proto @@ -89,11 +89,9 @@ type Message struct { func (x *Message) Reset() { *x = Message{} - if protoimpl.UnsafeEnabled { - mi := &file_core_capabilities_remote_types_messages_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_core_capabilities_remote_types_messages_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Message) String() string { @@ -104,7 +102,7 @@ func (*Message) ProtoMessage() {} func (x *Message) ProtoReflect() protoreflect.Message { mi := &file_core_capabilities_remote_types_messages_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -160,11 +158,9 @@ type MessageBody struct { func (x *MessageBody) Reset() { *x = MessageBody{} - if protoimpl.UnsafeEnabled { - mi := &file_core_capabilities_remote_types_messages_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_core_capabilities_remote_types_messages_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *MessageBody) String() string { @@ -175,7 +171,7 @@ func (*MessageBody) ProtoMessage() {} func (x *MessageBody) ProtoReflect() protoreflect.Message { mi := &file_core_capabilities_remote_types_messages_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -321,11 +317,9 @@ type TriggerRegistrationMetadata struct { func (x *TriggerRegistrationMetadata) Reset() { *x = TriggerRegistrationMetadata{} - if protoimpl.UnsafeEnabled { - mi := &file_core_capabilities_remote_types_messages_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_core_capabilities_remote_types_messages_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *TriggerRegistrationMetadata) String() string { @@ -336,7 +330,7 @@ func (*TriggerRegistrationMetadata) ProtoMessage() {} func (x *TriggerRegistrationMetadata) ProtoReflect() protoreflect.Message { mi := &file_core_capabilities_remote_types_messages_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -369,11 +363,9 @@ type TriggerEventMetadata struct { func (x *TriggerEventMetadata) Reset() { *x = TriggerEventMetadata{} - if protoimpl.UnsafeEnabled { - mi := &file_core_capabilities_remote_types_messages_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_core_capabilities_remote_types_messages_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *TriggerEventMetadata) String() string { @@ -384,7 +376,7 @@ func (*TriggerEventMetadata) ProtoMessage() {} func (x *TriggerEventMetadata) ProtoReflect() protoreflect.Message { mi := &file_core_capabilities_remote_types_messages_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -522,56 +514,6 @@ func file_core_capabilities_remote_types_messages_proto_init() { if File_core_capabilities_remote_types_messages_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_core_capabilities_remote_types_messages_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*Message); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_core_capabilities_remote_types_messages_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*MessageBody); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_core_capabilities_remote_types_messages_proto_msgTypes[2].Exporter = func(v any, i int) any { - switch v := v.(*TriggerRegistrationMetadata); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_core_capabilities_remote_types_messages_proto_msgTypes[3].Exporter = func(v any, i int) any { - switch v := v.(*TriggerEventMetadata); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } file_core_capabilities_remote_types_messages_proto_msgTypes[1].OneofWrappers = []any{ (*MessageBody_TriggerRegistrationMetadata)(nil), (*MessageBody_TriggerEventMetadata)(nil), diff --git a/core/capabilities/streams/consensus_agg_test.go b/core/capabilities/streams/consensus_agg_test.go index 04396a38ba9..834889f2c31 100644 --- a/core/capabilities/streams/consensus_agg_test.go +++ b/core/capabilities/streams/consensus_agg_test.go @@ -36,11 +36,11 @@ func TestStreamsConsensusAggregator(t *testing.T) { config := newAggConfig(t, feeds) lggr := logger.TestLogger(t) codec := streams.NewCodec(lggr) - agg, err := datafeeds.NewDataFeedsAggregator(*config, codec, lggr) + agg, err := datafeeds.NewDataFeedsAggregator(*config, codec) require.NoError(t, err) // init round - empty previous Outcome, empty observations - outcome, err := agg.Aggregate(nil, map[commontypes.OracleID][]values.Value{}, Fw) + outcome, err := agg.Aggregate(lggr, nil, map[commontypes.OracleID][]values.Value{}, Fw) require.NoError(t, err) require.False(t, outcome.ShouldReport) @@ -56,7 +56,7 @@ func TestStreamsConsensusAggregator(t *testing.T) { for c := 0; c < T; c++ { obs := newObservations(t, Nw, feeds, Ft+1, allowedSigners) processingStart := time.Now().UnixMilli() - outcome, err = agg.Aggregate(outcome, obs, Fw) + outcome, err = agg.Aggregate(lggr, outcome, obs, Fw) processingTime += time.Now().UnixMilli() - processingStart require.NoError(t, err) } diff --git a/core/chains/config.go b/core/chains/config.go index 3556c33a785..73e1e87f146 100644 --- a/core/chains/config.go +++ b/core/chains/config.go @@ -6,8 +6,9 @@ import ( var ( // ErrChainIDEmpty is returned when chain is required but was empty. - ErrChainIDEmpty = errors.New("chain id empty") - ErrNotFound = errors.New("not found") + ErrChainIDEmpty = errors.New("chain id empty") + ErrNotFound = errors.New("not found") + ErrMultipleChainFound = errors.New("multiple chains found with the same chain ID") ) // ChainOpts holds options for configuring a Chain diff --git a/core/chains/evm/client/chain_client.go b/core/chains/evm/client/chain_client.go index d5c9cd32bce..6334fb78423 100644 --- a/core/chains/evm/client/chain_client.go +++ b/core/chains/evm/client/chain_client.go @@ -3,6 +3,7 @@ package client import ( "context" "math/big" + "sync" "time" "github.com/ethereum/go-ethereum" @@ -12,9 +13,7 @@ import ( commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/logger" - commonclient "github.com/smartcontractkit/chainlink/v2/common/client" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -30,8 +29,6 @@ type Client interface { Close() // ChainID locally stored for quick access ConfiguredChainID() *big.Int - // ChainID RPC call - ChainID() (*big.Int, error) // NodeStates returns a map of node Name->node state // It might be nil or empty, e.g. for mock clients etc @@ -56,7 +53,7 @@ type Client interface { // correct hash from the RPC response. HeadByNumber(ctx context.Context, n *big.Int) (*evmtypes.Head, error) HeadByHash(ctx context.Context, n common.Hash) (*evmtypes.Head, error) - SubscribeNewHead(ctx context.Context, ch chan<- *evmtypes.Head) (ethereum.Subscription, error) + SubscribeToHeads(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) // LatestFinalizedBlock - returns the latest finalized block as it's returned from an RPC. // CAUTION: Using this method might cause local finality violations. It's highly recommended // to use HeadTracker to get latest finalized block. @@ -103,21 +100,11 @@ func ContextWithDefaultTimeout() (ctx context.Context, cancel context.CancelFunc } type chainClient struct { - multiNode commonclient.MultiNode[ + multiNode *commonclient.MultiNode[ *big.Int, - evmtypes.Nonce, - common.Address, - common.Hash, - *types.Transaction, - common.Hash, - types.Log, - ethereum.FilterQuery, - *evmtypes.Receipt, - *assets.Wei, - *evmtypes.Head, - RPCClient, - rpc.BatchElem, + *RPCClient, ] + txSender *commonclient.TransactionSender[*types.Transaction, *big.Int, *RPCClient] logger logger.SugaredLogger chainType chaintype.ChainType clientErrors evmconfig.ClientErrors @@ -127,31 +114,41 @@ func NewChainClient( lggr logger.Logger, selectionMode string, leaseDuration time.Duration, - noNewHeadsThreshold time.Duration, - nodes []commonclient.Node[*big.Int, *evmtypes.Head, RPCClient], - sendonlys []commonclient.SendOnlyNode[*big.Int, RPCClient], + nodes []commonclient.Node[*big.Int, *RPCClient], + sendonlys []commonclient.SendOnlyNode[*big.Int, *RPCClient], chainID *big.Int, - chainType chaintype.ChainType, clientErrors evmconfig.ClientErrors, deathDeclarationDelay time.Duration, + chainType chaintype.ChainType, ) Client { - multiNode := commonclient.NewMultiNode( + chainFamily := "EVM" + multiNode := commonclient.NewMultiNode[*big.Int, *RPCClient]( lggr, selectionMode, leaseDuration, - noNewHeadsThreshold, nodes, sendonlys, chainID, - "EVM", - func(tx *types.Transaction, err error) commonclient.SendTxReturnCode { - return ClassifySendError(err, clientErrors, logger.Sugared(logger.Nop()), tx, common.Address{}, chainType.IsL2()) - }, - 0, // use the default value provided by the implementation + chainFamily, deathDeclarationDelay, ) + + classifySendError := func(tx *types.Transaction, err error) commonclient.SendTxReturnCode { + return ClassifySendError(err, clientErrors, logger.Sugared(logger.Nop()), tx, common.Address{}, chainType.IsL2()) + } + + txSender := commonclient.NewTransactionSender[*types.Transaction, *big.Int, *RPCClient]( + lggr, + chainID, + chainFamily, + multiNode, + classifySendError, + 0, // use the default value provided by the implementation + ) + return &chainClient{ multiNode: multiNode, + txSender: txSender, logger: logger.Sugared(lggr), chainType: chainType, clientErrors: clientErrors, @@ -159,7 +156,11 @@ func NewChainClient( } func (c *chainClient) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { - return c.multiNode.BalanceAt(ctx, account, blockNumber) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.BalanceAt(ctx, account, blockNumber) } // BatchCallContext - sends all given requests as a single batch. @@ -170,103 +171,170 @@ func (c *chainClient) BalanceAt(ctx context.Context, account common.Address, blo // might not be properly handled and returned results might have weaker finality guarantees. It's highly recommended // to use HeadTracker to identify latest finalized block. func (c *chainClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { - return c.multiNode.BatchCallContext(ctx, b) + r, err := c.multiNode.SelectRPC() + if err != nil { + return err + } + return r.BatchCallContext(ctx, b) } // Similar to BatchCallContext, ensure the provided BatchElem slice is passed through func (c *chainClient) BatchCallContextAll(ctx context.Context, b []rpc.BatchElem) error { if c.chainType == chaintype.ChainHedera { - activeRPC, err := c.multiNode.SelectNodeRPC() + activeRPC, err := c.multiNode.SelectRPC() if err != nil { return err } return activeRPC.BatchCallContext(ctx, b) } - return c.multiNode.BatchCallContextAll(ctx, b) + + var wg sync.WaitGroup + defer wg.Wait() + + // Select main RPC to use for return value + main, selectionErr := c.multiNode.SelectRPC() + + doFunc := func(ctx context.Context, rpc *RPCClient, isSendOnly bool) { + if rpc == main { + return + } + // Parallel call made to all other nodes with ignored return value + wg.Add(1) + go func(rpc *RPCClient) { + defer wg.Done() + err := rpc.BatchCallContext(ctx, b) + if err != nil { + c.logger.Debugw("Secondary node BatchCallContext failed", "err", err) + } else { + c.logger.Debug("Secondary node BatchCallContext success") + } + }(rpc) + } + + if err := c.multiNode.DoAll(ctx, doFunc); err != nil { + return err + } + + if selectionErr != nil { + return selectionErr + } + + return main.BatchCallContext(ctx, b) } // TODO-1663: return custom Block type instead of geth's once client.go is deprecated. func (c *chainClient) BlockByHash(ctx context.Context, hash common.Hash) (b *types.Block, err error) { - rpc, err := c.multiNode.SelectNodeRPC() + r, err := c.multiNode.SelectRPC() if err != nil { return b, err } - return rpc.BlockByHashGeth(ctx, hash) + return r.BlockByHashGeth(ctx, hash) } // TODO-1663: return custom Block type instead of geth's once client.go is deprecated. func (c *chainClient) BlockByNumber(ctx context.Context, number *big.Int) (b *types.Block, err error) { - rpc, err := c.multiNode.SelectNodeRPC() + r, err := c.multiNode.SelectRPC() if err != nil { return b, err } - return rpc.BlockByNumberGeth(ctx, number) + return r.BlockByNumberGeth(ctx, number) } func (c *chainClient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { - return c.multiNode.CallContext(ctx, result, method, args...) + r, err := c.multiNode.SelectRPC() + if err != nil { + return err + } + return r.CallContext(ctx, result, method, args...) } func (c *chainClient) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { - return c.multiNode.CallContract(ctx, msg, blockNumber) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.CallContract(ctx, msg, blockNumber) } func (c *chainClient) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) { - return c.multiNode.PendingCallContract(ctx, msg) -} - -// TODO-1663: change this to actual ChainID() call once client.go is deprecated. -func (c *chainClient) ChainID() (*big.Int, error) { - //return c.multiNode.ChainID(ctx), nil - return c.multiNode.ConfiguredChainID(), nil + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.PendingCallContract(ctx, msg) } func (c *chainClient) Close() { - c.multiNode.Close() + _ = c.txSender.Close() + _ = c.multiNode.Close() } func (c *chainClient) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { - return c.multiNode.CodeAt(ctx, account, blockNumber) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.CodeAt(ctx, account, blockNumber) } func (c *chainClient) ConfiguredChainID() *big.Int { - return c.multiNode.ConfiguredChainID() + return c.multiNode.ChainID() } func (c *chainClient) Dial(ctx context.Context) error { - return c.multiNode.Dial(ctx) + err := c.multiNode.Start(ctx) + if err != nil { + return err + } + return c.txSender.Start(ctx) } func (c *chainClient) EstimateGas(ctx context.Context, call ethereum.CallMsg) (uint64, error) { - return c.multiNode.EstimateGas(ctx, call) + r, err := c.multiNode.SelectRPC() + if err != nil { + return 0, err + } + return r.EstimateGas(ctx, call) } func (c *chainClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { - return c.multiNode.FilterEvents(ctx, q) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.FilterEvents(ctx, q) } func (c *chainClient) HeaderByHash(ctx context.Context, h common.Hash) (head *types.Header, err error) { - rpc, err := c.multiNode.SelectNodeRPC() + r, err := c.multiNode.SelectRPC() if err != nil { return head, err } - return rpc.HeaderByHash(ctx, h) + return r.HeaderByHash(ctx, h) } func (c *chainClient) HeaderByNumber(ctx context.Context, n *big.Int) (head *types.Header, err error) { - rpc, err := c.multiNode.SelectNodeRPC() + r, err := c.multiNode.SelectRPC() if err != nil { return head, err } - return rpc.HeaderByNumber(ctx, n) + return r.HeaderByNumber(ctx, n) } func (c *chainClient) HeadByHash(ctx context.Context, h common.Hash) (*evmtypes.Head, error) { - return c.multiNode.BlockByHash(ctx, h) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.BlockByHash(ctx, h) } func (c *chainClient) HeadByNumber(ctx context.Context, n *big.Int) (*evmtypes.Head, error) { - return c.multiNode.BlockByNumber(ctx, n) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.BlockByNumber(ctx, n) } func (c *chainClient) IsL2() bool { @@ -274,11 +342,19 @@ func (c *chainClient) IsL2() bool { } func (c *chainClient) LINKBalance(ctx context.Context, address common.Address, linkAddress common.Address) (*commonassets.Link, error) { - return c.multiNode.LINKBalance(ctx, address, linkAddress) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.LINKBalance(ctx, address, linkAddress) } func (c *chainClient) LatestBlockHeight(ctx context.Context) (*big.Int, error) { - return c.multiNode.LatestBlockHeight(ctx) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.LatestBlockHeight(ctx) } func (c *chainClient) NodeStates() map[string]string { @@ -286,28 +362,33 @@ func (c *chainClient) NodeStates() map[string]string { } func (c *chainClient) PendingCodeAt(ctx context.Context, account common.Address) (b []byte, err error) { - rpc, err := c.multiNode.SelectNodeRPC() + r, err := c.multiNode.SelectRPC() if err != nil { return b, err } - return rpc.PendingCodeAt(ctx, account) + return r.PendingCodeAt(ctx, account) } // TODO-1663: change this to evmtypes.Nonce(int64) once client.go is deprecated. func (c *chainClient) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { - n, err := c.multiNode.PendingSequenceAt(ctx, account) + r, err := c.multiNode.SelectRPC() + if err != nil { + return 0, err + } + n, err := r.PendingSequenceAt(ctx, account) return uint64(n), err } func (c *chainClient) SendTransaction(ctx context.Context, tx *types.Transaction) error { if c.chainType == chaintype.ChainHedera { - activeRPC, err := c.multiNode.SelectNodeRPC() + activeRPC, err := c.multiNode.SelectRPC() if err != nil { return err } return activeRPC.SendTransaction(ctx, tx) } - return c.multiNode.SendTransaction(ctx, tx) + _, err := c.txSender.SendTransaction(ctx, tx) + return err } func (c *chainClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error) { @@ -317,65 +398,91 @@ func (c *chainClient) SendTransactionReturnCode(ctx context.Context, tx *types.T } func (c *chainClient) SequenceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (evmtypes.Nonce, error) { - return c.multiNode.SequenceAt(ctx, account, blockNumber) + r, err := c.multiNode.SelectRPC() + if err != nil { + return 0, err + } + return r.SequenceAt(ctx, account, blockNumber) } func (c *chainClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (s ethereum.Subscription, err error) { - rpc, err := c.multiNode.SelectNodeRPC() + r, err := c.multiNode.SelectRPC() if err != nil { return s, err } - return rpc.SubscribeFilterLogs(ctx, q, ch) + return r.SubscribeFilterLogs(ctx, q, ch) } -func (c *chainClient) SubscribeNewHead(ctx context.Context, ch chan<- *evmtypes.Head) (ethereum.Subscription, error) { - return c.multiNode.SubscribeNewHead(ctx, ch) +func (c *chainClient) SubscribeToHeads(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, nil, err + } + + ch, sub, err := r.SubscribeToHeads(ctx) + if err != nil { + return nil, nil, err + } + + return ch, sub, nil } func (c *chainClient) SuggestGasPrice(ctx context.Context) (p *big.Int, err error) { - rpc, err := c.multiNode.SelectNodeRPC() + r, err := c.multiNode.SelectRPC() if err != nil { return p, err } - return rpc.SuggestGasPrice(ctx) + return r.SuggestGasPrice(ctx) } func (c *chainClient) SuggestGasTipCap(ctx context.Context) (t *big.Int, err error) { - rpc, err := c.multiNode.SelectNodeRPC() + r, err := c.multiNode.SelectRPC() if err != nil { return t, err } - return rpc.SuggestGasTipCap(ctx) + return r.SuggestGasTipCap(ctx) } func (c *chainClient) TokenBalance(ctx context.Context, address common.Address, contractAddress common.Address) (*big.Int, error) { - return c.multiNode.TokenBalance(ctx, address, contractAddress) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.TokenBalance(ctx, address, contractAddress) } func (c *chainClient) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, error) { - return c.multiNode.TransactionByHash(ctx, txHash) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.TransactionByHash(ctx, txHash) } // TODO-1663: return custom Receipt type instead of geth's once client.go is deprecated. -func (c *chainClient) TransactionReceipt(ctx context.Context, txHash common.Hash) (r *types.Receipt, err error) { - rpc, err := c.multiNode.SelectNodeRPC() +func (c *chainClient) TransactionReceipt(ctx context.Context, txHash common.Hash) (receipt *types.Receipt, err error) { + r, err := c.multiNode.SelectRPC() if err != nil { - return r, err + return receipt, err } //return rpc.TransactionReceipt(ctx, txHash) - return rpc.TransactionReceiptGeth(ctx, txHash) + return r.TransactionReceiptGeth(ctx, txHash) } func (c *chainClient) LatestFinalizedBlock(ctx context.Context) (*evmtypes.Head, error) { - return c.multiNode.LatestFinalizedBlock(ctx) + r, err := c.multiNode.SelectRPC() + if err != nil { + return nil, err + } + return r.LatestFinalizedBlock(ctx) } func (c *chainClient) FeeHistory(ctx context.Context, blockCount uint64, rewardPercentiles []float64) (feeHistory *ethereum.FeeHistory, err error) { - rpc, err := c.multiNode.SelectNodeRPC() + r, err := c.multiNode.SelectRPC() if err != nil { return feeHistory, err } - return rpc.FeeHistory(ctx, blockCount, rewardPercentiles) + return r.FeeHistory(ctx, blockCount, rewardPercentiles) } func (c *chainClient) CheckTxValidity(ctx context.Context, from common.Address, to common.Address, data []byte) *SendError { diff --git a/core/chains/evm/client/chain_client_test.go b/core/chains/evm/client/chain_client_test.go index f6041228214..5562046d96a 100644 --- a/core/chains/evm/client/chain_client_test.go +++ b/core/chains/evm/client/chain_client_test.go @@ -21,16 +21,13 @@ import ( "github.com/ethereum/go-ethereum/rpc" pkgerrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/tidwall/gjson" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/testutils" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" @@ -451,6 +448,20 @@ func TestEthClient_SendTransaction_WithSecondaryURLs(t *testing.T) { require.Eventually(t, func() bool { return service.sentCount.Load() == int32(2) }, tests.WaitTimeout(t), 500*time.Millisecond) } +type sendTxService struct { + chainID *big.Int + sentCount atomic.Int32 +} + +func (x *sendTxService) ChainID(ctx context.Context) (*hexutil.Big, error) { + return (*hexutil.Big)(x.chainID), nil +} + +func (x *sendTxService) SendRawTransaction(ctx context.Context, signRawTx hexutil.Bytes) error { + x.sentCount.Add(1) + return nil +} + func TestEthClient_SendTransactionReturnCode(t *testing.T) { t.Parallel() @@ -691,20 +702,6 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { }) } -type sendTxService struct { - chainID *big.Int - sentCount atomic.Int32 -} - -func (x *sendTxService) ChainId(ctx context.Context) (*hexutil.Big, error) { - return (*hexutil.Big)(x.chainID), nil -} - -func (x *sendTxService) SendRawTransaction(ctx context.Context, signRawTx hexutil.Bytes) error { - x.sentCount.Add(1) - return nil -} - func TestEthClient_SubscribeNewHead(t *testing.T) { t.Parallel() @@ -729,8 +726,7 @@ func TestEthClient_SubscribeNewHead(t *testing.T) { err := ethClient.Dial(tests.Context(t)) require.NoError(t, err) - headCh := make(chan *evmtypes.Head) - sub, err := ethClient.SubscribeNewHead(ctx, headCh) + headCh, sub, err := ethClient.SubscribeToHeads(ctx) require.NoError(t, err) select { @@ -745,55 +741,49 @@ func TestEthClient_SubscribeNewHead(t *testing.T) { sub.Unsubscribe() } -func newMockRpc(t *testing.T) *mocks.RPCClient { - mockRpc := mocks.NewRPCClient(t) - mockRpc.On("Dial", mock.Anything).Return(nil).Once() - mockRpc.On("Close").Return(nil).Once() - mockRpc.On("ChainID", mock.Anything).Return(testutils.FixtureChainID, nil).Once() - // node does not always manage to fully setup aliveLoop, so we have to make calls optional to avoid flakes - mockRpc.On("SubscribeToHeads", mock.Anything).Return(nil, client.NewMockSubscription(), nil).Maybe() - mockRpc.On("SetAliveLoopSub", mock.Anything).Return().Maybe() - return mockRpc -} - -func TestChainClient_BatchCallContext(t *testing.T) { +func TestEthClient_BatchCallContext(t *testing.T) { t.Parallel() t.Run("batch requests return errors", func(t *testing.T) { - ctx := tests.Context(t) rpcError := errors.New("something went wrong") - blockNumResp := "" - blockNum := hexutil.EncodeBig(big.NewInt(42)) b := []rpc.BatchElem{ { - Method: "eth_getBlockByNumber", - Args: []interface{}{blockNum, true}, - Result: &types.Block{}, + Method: "eth_call", + Args: []interface{}{0}, + Result: "", }, { - Method: "eth_blockNumber", - Result: &blockNumResp, + Method: "eth_call", + Args: []interface{}{1}, + Result: "", }, } - mockRpc := newMockRpc(t) - mockRpc.On("GetInterceptedChainInfo").Return(commonclient.ChainInfo{}, commonclient.ChainInfo{}).Maybe() - mockRpc.On("BatchCallContext", mock.Anything, b).Run(func(args mock.Arguments) { - reqs := args.Get(1).([]rpc.BatchElem) - for i := 0; i < len(reqs); i++ { - elem := &reqs[i] - elem.Error = rpcError + wsURL := testutils.NewWSServer(t, testutils.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + switch method { + case "eth_subscribe": + resp.Result = `"0x00"` + resp.Notify = headResult + return + case "eth_unsubscribe": + resp.Result = "true" + return } - }).Return(nil).Once() + require.Equal(t, "eth_call", method) + resp.Error.Code = -1 + resp.Error.Message = rpcError.Error() + resp.Result = "" + return + }).WSURL().String() - client := client.NewChainClientWithMockedRpc(t, commonclient.NodeSelectionModeRoundRobin, time.Second*0, time.Second*0, testutils.FixtureChainID, mockRpc) - err := client.Dial(ctx) + ethClient := mustNewChainClient(t, wsURL) + err := ethClient.Dial(tests.Context(t)) require.NoError(t, err) - err = client.BatchCallContext(ctx, b) + err = ethClient.BatchCallContext(context.Background(), b) require.NoError(t, err) for _, elem := range b { - require.ErrorIs(t, rpcError, elem.Error) + require.Equal(t, elem.Error.Error(), rpcError.Error()) } }) } @@ -826,11 +816,8 @@ func TestEthClient_ErroringClient(t *testing.T) { _, err = erroringClient.CallContract(ctx, ethereum.CallMsg{}, nil) require.Equal(t, err, commonclient.ErroringNodeError) - // TODO-1663: test actual ChainID() call once client.go is deprecated. - id, err := erroringClient.ChainID() - require.Equal(t, id, testutils.FixtureChainID) - //require.Equal(t, err, commonclient.ErroringNodeError) - require.Equal(t, err, nil) + id := erroringClient.ConfiguredChainID() + require.Equal(t, id, big.NewInt(0)) _, err = erroringClient.CodeAt(ctx, common.Address{}, nil) require.Equal(t, err, commonclient.ErroringNodeError) @@ -871,13 +858,14 @@ func TestEthClient_ErroringClient(t *testing.T) { _, err = erroringClient.PendingNonceAt(ctx, common.Address{}) require.Equal(t, err, commonclient.ErroringNodeError) + txSenderNotStarted := errors.New("TransactionSender not started") err = erroringClient.SendTransaction(ctx, nil) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, err, txSenderNotStarted) tx := testutils.NewLegacyTransaction(uint64(42), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) code, err := erroringClient.SendTransactionReturnCode(ctx, tx, common.Address{}) - require.Equal(t, code, commonclient.Retryable) - require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, code, commonclient.Unknown) + require.Equal(t, err, txSenderNotStarted) _, err = erroringClient.SequenceAt(ctx, common.Address{}, nil) require.Equal(t, err, commonclient.ErroringNodeError) @@ -885,7 +873,7 @@ func TestEthClient_ErroringClient(t *testing.T) { _, err = erroringClient.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, nil) require.Equal(t, err, commonclient.ErroringNodeError) - _, err = erroringClient.SubscribeNewHead(ctx, nil) + _, _, err = erroringClient.SubscribeToHeads(ctx) require.Equal(t, err, commonclient.ErroringNodeError) _, err = erroringClient.SuggestGasPrice(ctx) diff --git a/core/chains/evm/client/config_builder_test.go b/core/chains/evm/client/config_builder_test.go index 22956fb0185..856242edada 100644 --- a/core/chains/evm/client/config_builder_test.go +++ b/core/chains/evm/client/config_builder_test.go @@ -69,7 +69,7 @@ func TestClientConfigBuilder(t *testing.T) { require.Equal(t, noNewFinalizedBlocksThreshold, chainCfg.NoNewFinalizedHeadsThreshold()) // let combiler tell us, when we do not have sufficient data to create evm client - _ = client.NewEvmClient(nodePool, chainCfg, nil, logger.Test(t), big.NewInt(10), nodes, chaintype.ChainType(chainTypeStr)) + _, _ = client.NewEvmClient(nodePool, chainCfg, nil, logger.Test(t), big.NewInt(10), nodes, chaintype.ChainType(chainTypeStr)) } func TestNodeConfigs(t *testing.T) { diff --git a/core/chains/evm/client/evm_client.go b/core/chains/evm/client/evm_client.go index c596bbc3a95..18206265fd7 100644 --- a/core/chains/evm/client/evm_client.go +++ b/core/chains/evm/client/evm_client.go @@ -11,36 +11,32 @@ import ( evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" - evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ) -func NewEvmClient(cfg evmconfig.NodePool, chainCfg commonclient.ChainConfig, clientErrors evmconfig.ClientErrors, lggr logger.Logger, chainID *big.Int, nodes []*toml.Node, chainType chaintype.ChainType) Client { - var primaries []commonclient.Node[*big.Int, *evmtypes.Head, RPCClient] - var sendonlys []commonclient.SendOnlyNode[*big.Int, RPCClient] +func NewEvmClient(cfg evmconfig.NodePool, chainCfg commonclient.ChainConfig, clientErrors evmconfig.ClientErrors, lggr logger.Logger, chainID *big.Int, nodes []*toml.Node, chainType chaintype.ChainType) (Client, error) { + var primaries []commonclient.Node[*big.Int, *RPCClient] + var sendonlys []commonclient.SendOnlyNode[*big.Int, *RPCClient] largePayloadRPCTimeout, defaultRPCTimeout := getRPCTimeouts(chainType) + for i, node := range nodes { - var ws *url.URL - if node.WSURL != nil { - ws = (*url.URL)(node.WSURL) - } if node.SendOnly != nil && *node.SendOnly { - rpc := NewRPCClient(lggr, nil, (*url.URL)(node.HTTPURL), *node.Name, i, chainID, - commonclient.Secondary, cfg.FinalizedBlockPollInterval(), cfg.NewHeadsPollInterval(), largePayloadRPCTimeout, defaultRPCTimeout, chainType) + rpc := NewRPCClient(cfg, lggr, nil, node.HTTPURL.URL(), *node.Name, i, chainID, + commonclient.Secondary, largePayloadRPCTimeout, defaultRPCTimeout, chainType) sendonly := commonclient.NewSendOnlyNode(lggr, (url.URL)(*node.HTTPURL), *node.Name, chainID, rpc) sendonlys = append(sendonlys, sendonly) } else { - rpc := NewRPCClient(lggr, ws, (*url.URL)(node.HTTPURL), *node.Name, i, - chainID, commonclient.Primary, cfg.FinalizedBlockPollInterval(), cfg.NewHeadsPollInterval(), largePayloadRPCTimeout, defaultRPCTimeout, chainType) + rpc := NewRPCClient(cfg, lggr, node.WSURL.URL(), node.HTTPURL.URL(), *node.Name, i, + chainID, commonclient.Primary, largePayloadRPCTimeout, defaultRPCTimeout, chainType) primaryNode := commonclient.NewNode(cfg, chainCfg, - lggr, ws, (*url.URL)(node.HTTPURL), *node.Name, i, chainID, *node.Order, + lggr, node.WSURL.URL(), node.HTTPURL.URL(), *node.Name, i, chainID, *node.Order, rpc, "EVM") primaries = append(primaries, primaryNode) } } - return NewChainClient(lggr, cfg.SelectionMode(), cfg.LeaseDuration(), chainCfg.NodeNoNewHeadsThreshold(), - primaries, sendonlys, chainID, chainType, clientErrors, cfg.DeathDeclarationDelay()) + return NewChainClient(lggr, cfg.SelectionMode(), cfg.LeaseDuration(), + primaries, sendonlys, chainID, clientErrors, cfg.DeathDeclarationDelay(), chainType), nil } func getRPCTimeouts(chainType chaintype.ChainType) (largePayload, defaultTimeout time.Duration) { diff --git a/core/chains/evm/client/evm_client_test.go b/core/chains/evm/client/evm_client_test.go index b762c14653c..9deca8edeee 100644 --- a/core/chains/evm/client/evm_client_test.go +++ b/core/chains/evm/client/evm_client_test.go @@ -45,6 +45,7 @@ func TestNewEvmClient(t *testing.T) { finalizedBlockPollInterval, newHeadsPollInterval) require.NoError(t, err) - client := client.NewEvmClient(nodePool, chainCfg, nil, logger.Test(t), testutils.FixtureChainID, nodes, chaintype.ChainType(chainTypeStr)) + client, err := client.NewEvmClient(nodePool, chainCfg, nil, logger.Test(t), testutils.FixtureChainID, nodes, chaintype.ChainType(chainTypeStr)) require.NotNil(t, client) + require.NoError(t, err) } diff --git a/core/chains/evm/client/helpers_test.go b/core/chains/evm/client/helpers_test.go index 1a6090e4a01..f9751be765c 100644 --- a/core/chains/evm/client/helpers_test.go +++ b/core/chains/evm/client/helpers_test.go @@ -14,7 +14,6 @@ import ( commonclient "github.com/smartcontractkit/chainlink/v2/common/client" clientMocks "github.com/smartcontractkit/chainlink/v2/common/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ) @@ -148,27 +147,28 @@ func NewChainClientWithTestNode( } lggr := logger.Test(t) - rpc := NewRPCClient(lggr, parsed, rpcHTTPURL, "eth-primary-rpc-0", id, chainID, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + nodePoolCfg := TestNodePoolConfig{ + NodeFinalizedBlockPollInterval: 1 * time.Second, + } + rpc := NewRPCClient(nodePoolCfg, lggr, parsed, rpcHTTPURL, "eth-primary-rpc-0", id, chainID, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") - n := commonclient.NewNode[*big.Int, *evmtypes.Head, RPCClient]( + n := commonclient.NewNode[*big.Int, *evmtypes.Head, *RPCClient]( nodeCfg, clientMocks.ChainConfig{NoNewHeadsThresholdVal: noNewHeadsThreshold}, lggr, parsed, rpcHTTPURL, "eth-primary-node-0", id, chainID, 1, rpc, "EVM") - primaries := []commonclient.Node[*big.Int, *evmtypes.Head, RPCClient]{n} + primaries := []commonclient.Node[*big.Int, *RPCClient]{n} - var sendonlys []commonclient.SendOnlyNode[*big.Int, RPCClient] + var sendonlys []commonclient.SendOnlyNode[*big.Int, *RPCClient] for i, u := range sendonlyRPCURLs { if u.Scheme != "http" && u.Scheme != "https" { return nil, pkgerrors.Errorf("sendonly ethereum rpc url scheme must be http(s): %s", u.String()) } - var empty url.URL - rpc := NewRPCClient(lggr, &empty, &sendonlyRPCURLs[i], fmt.Sprintf("eth-sendonly-rpc-%d", i), id, chainID, commonclient.Secondary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") - s := commonclient.NewSendOnlyNode[*big.Int, RPCClient]( + rpc := NewRPCClient(nodePoolCfg, lggr, nil, &sendonlyRPCURLs[i], fmt.Sprintf("eth-sendonly-rpc-%d", i), id, chainID, commonclient.Secondary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + s := commonclient.NewSendOnlyNode[*big.Int, *RPCClient]( lggr, u, fmt.Sprintf("eth-sendonly-%d", i), chainID, rpc) sendonlys = append(sendonlys, s) } - var chainType chaintype.ChainType clientErrors := NewTestClientErrors() - c := NewChainClient(lggr, nodeCfg.SelectionMode(), leaseDuration, noNewHeadsThreshold, primaries, sendonlys, chainID, chainType, &clientErrors, 0) + c := NewChainClient(lggr, nodeCfg.SelectionMode(), leaseDuration, primaries, sendonlys, chainID, &clientErrors, 0, "") t.Cleanup(c.Close) return c, nil } @@ -182,8 +182,7 @@ func NewChainClientWithEmptyNode( ) Client { lggr := logger.Test(t) - var chainType chaintype.ChainType - c := NewChainClient(lggr, selectionMode, leaseDuration, noNewHeadsThreshold, nil, nil, chainID, chainType, nil, 0) + c := NewChainClient(lggr, selectionMode, leaseDuration, nil, nil, chainID, nil, 0, "") t.Cleanup(c.Close) return c } @@ -194,22 +193,20 @@ func NewChainClientWithMockedRpc( leaseDuration time.Duration, noNewHeadsThreshold time.Duration, chainID *big.Int, - rpc RPCClient, + rpc *RPCClient, ) Client { lggr := logger.Test(t) - var chainType chaintype.ChainType - cfg := TestNodePoolConfig{ NodeSelectionMode: commonclient.NodeSelectionModeRoundRobin, } parsed, _ := url.ParseRequestURI("ws://test") - n := commonclient.NewNode[*big.Int, *evmtypes.Head, RPCClient]( + n := commonclient.NewNode[*big.Int, *evmtypes.Head, *RPCClient]( cfg, clientMocks.ChainConfig{NoNewHeadsThresholdVal: noNewHeadsThreshold}, lggr, parsed, nil, "eth-primary-node-0", 1, chainID, 1, rpc, "EVM") - primaries := []commonclient.Node[*big.Int, *evmtypes.Head, RPCClient]{n} + primaries := []commonclient.Node[*big.Int, *RPCClient]{n} clientErrors := NewTestClientErrors() - c := NewChainClient(lggr, selectionMode, leaseDuration, noNewHeadsThreshold, primaries, nil, chainID, chainType, &clientErrors, 0) + c := NewChainClient(lggr, selectionMode, leaseDuration, primaries, nil, chainID, &clientErrors, 0, "") t.Cleanup(c.Close) return c } diff --git a/core/chains/evm/client/mocks/client.go b/core/chains/evm/client/mocks/client.go index da034d95774..e3350cf8a14 100644 --- a/core/chains/evm/client/mocks/client.go +++ b/core/chains/evm/client/mocks/client.go @@ -430,63 +430,6 @@ func (_c *Client_CallContract_Call) RunAndReturn(run func(context.Context, ether return _c } -// ChainID provides a mock function with given fields: -func (_m *Client) ChainID() (*big.Int, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for ChainID") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func() (*big.Int, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() *big.Int); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Client_ChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChainID' -type Client_ChainID_Call struct { - *mock.Call -} - -// ChainID is a helper method to define mock.On call -func (_e *Client_Expecter) ChainID() *Client_ChainID_Call { - return &Client_ChainID_Call{Call: _e.mock.On("ChainID")} -} - -func (_c *Client_ChainID_Call) Run(run func()) *Client_ChainID_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *Client_ChainID_Call) Return(_a0 *big.Int, _a1 error) *Client_ChainID_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *Client_ChainID_Call) RunAndReturn(run func() (*big.Int, error)) *Client_ChainID_Call { - _c.Call.Return(run) - return _c -} - // CheckTxValidity provides a mock function with given fields: ctx, from, to, data func (_m *Client) CheckTxValidity(ctx context.Context, from common.Address, to common.Address, data []byte) *client.SendError { ret := _m.Called(ctx, from, to, data) @@ -1801,61 +1744,69 @@ func (_c *Client_SubscribeFilterLogs_Call) RunAndReturn(run func(context.Context return _c } -// SubscribeNewHead provides a mock function with given fields: ctx, ch -func (_m *Client) SubscribeNewHead(ctx context.Context, ch chan<- *evmtypes.Head) (ethereum.Subscription, error) { - ret := _m.Called(ctx, ch) +// SubscribeToHeads provides a mock function with given fields: ctx +func (_m *Client) SubscribeToHeads(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { + ret := _m.Called(ctx) if len(ret) == 0 { - panic("no return value specified for SubscribeNewHead") + panic("no return value specified for SubscribeToHeads") } - var r0 ethereum.Subscription - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, chan<- *evmtypes.Head) (ethereum.Subscription, error)); ok { - return rf(ctx, ch) + var r0 <-chan *evmtypes.Head + var r1 ethereum.Subscription + var r2 error + if rf, ok := ret.Get(0).(func(context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error)); ok { + return rf(ctx) } - if rf, ok := ret.Get(0).(func(context.Context, chan<- *evmtypes.Head) ethereum.Subscription); ok { - r0 = rf(ctx, ch) + if rf, ok := ret.Get(0).(func(context.Context) <-chan *evmtypes.Head); ok { + r0 = rf(ctx) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(ethereum.Subscription) + r0 = ret.Get(0).(<-chan *evmtypes.Head) } } - if rf, ok := ret.Get(1).(func(context.Context, chan<- *evmtypes.Head) error); ok { - r1 = rf(ctx, ch) + if rf, ok := ret.Get(1).(func(context.Context) ethereum.Subscription); ok { + r1 = rf(ctx) } else { - r1 = ret.Error(1) + if ret.Get(1) != nil { + r1 = ret.Get(1).(ethereum.Subscription) + } } - return r0, r1 + if rf, ok := ret.Get(2).(func(context.Context) error); ok { + r2 = rf(ctx) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 } -// Client_SubscribeNewHead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeNewHead' -type Client_SubscribeNewHead_Call struct { +// Client_SubscribeToHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToHeads' +type Client_SubscribeToHeads_Call struct { *mock.Call } -// SubscribeNewHead is a helper method to define mock.On call +// SubscribeToHeads is a helper method to define mock.On call // - ctx context.Context -// - ch chan<- *evmtypes.Head -func (_e *Client_Expecter) SubscribeNewHead(ctx interface{}, ch interface{}) *Client_SubscribeNewHead_Call { - return &Client_SubscribeNewHead_Call{Call: _e.mock.On("SubscribeNewHead", ctx, ch)} +func (_e *Client_Expecter) SubscribeToHeads(ctx interface{}) *Client_SubscribeToHeads_Call { + return &Client_SubscribeToHeads_Call{Call: _e.mock.On("SubscribeToHeads", ctx)} } -func (_c *Client_SubscribeNewHead_Call) Run(run func(ctx context.Context, ch chan<- *evmtypes.Head)) *Client_SubscribeNewHead_Call { +func (_c *Client_SubscribeToHeads_Call) Run(run func(ctx context.Context)) *Client_SubscribeToHeads_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(chan<- *evmtypes.Head)) + run(args[0].(context.Context)) }) return _c } -func (_c *Client_SubscribeNewHead_Call) Return(_a0 ethereum.Subscription, _a1 error) *Client_SubscribeNewHead_Call { - _c.Call.Return(_a0, _a1) +func (_c *Client_SubscribeToHeads_Call) Return(_a0 <-chan *evmtypes.Head, _a1 ethereum.Subscription, _a2 error) *Client_SubscribeToHeads_Call { + _c.Call.Return(_a0, _a1, _a2) return _c } -func (_c *Client_SubscribeNewHead_Call) RunAndReturn(run func(context.Context, chan<- *evmtypes.Head) (ethereum.Subscription, error)) *Client_SubscribeNewHead_Call { +func (_c *Client_SubscribeToHeads_Call) RunAndReturn(run func(context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error)) *Client_SubscribeToHeads_Call { _c.Call.Return(run) return _c } diff --git a/core/chains/evm/client/mocks/rpc_client.go b/core/chains/evm/client/mocks/rpc_client.go deleted file mode 100644 index 5567b3f8978..00000000000 --- a/core/chains/evm/client/mocks/rpc_client.go +++ /dev/null @@ -1,2532 +0,0 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. - -package mocks - -import ( - big "math/big" - - assets "github.com/smartcontractkit/chainlink-common/pkg/assets" - - common "github.com/ethereum/go-ethereum/common" - - commonclient "github.com/smartcontractkit/chainlink/v2/common/client" - - commontypes "github.com/smartcontractkit/chainlink/v2/common/types" - - context "context" - - coretypes "github.com/ethereum/go-ethereum/core/types" - - ethereum "github.com/ethereum/go-ethereum" - - evmassets "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" - - mock "github.com/stretchr/testify/mock" - - rpc "github.com/ethereum/go-ethereum/rpc" - - types "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" -) - -// RPCClient is an autogenerated mock type for the RPCClient type -type RPCClient struct { - mock.Mock -} - -type RPCClient_Expecter struct { - mock *mock.Mock -} - -func (_m *RPCClient) EXPECT() *RPCClient_Expecter { - return &RPCClient_Expecter{mock: &_m.Mock} -} - -// BalanceAt provides a mock function with given fields: ctx, accountAddress, blockNumber -func (_m *RPCClient) BalanceAt(ctx context.Context, accountAddress common.Address, blockNumber *big.Int) (*big.Int, error) { - ret := _m.Called(ctx, accountAddress, blockNumber) - - if len(ret) == 0 { - panic("no return value specified for BalanceAt") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) (*big.Int, error)); ok { - return rf(ctx, accountAddress, blockNumber) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) *big.Int); ok { - r0 = rf(ctx, accountAddress, blockNumber) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Address, *big.Int) error); ok { - r1 = rf(ctx, accountAddress, blockNumber) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_BalanceAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BalanceAt' -type RPCClient_BalanceAt_Call struct { - *mock.Call -} - -// BalanceAt is a helper method to define mock.On call -// - ctx context.Context -// - accountAddress common.Address -// - blockNumber *big.Int -func (_e *RPCClient_Expecter) BalanceAt(ctx interface{}, accountAddress interface{}, blockNumber interface{}) *RPCClient_BalanceAt_Call { - return &RPCClient_BalanceAt_Call{Call: _e.mock.On("BalanceAt", ctx, accountAddress, blockNumber)} -} - -func (_c *RPCClient_BalanceAt_Call) Run(run func(ctx context.Context, accountAddress common.Address, blockNumber *big.Int)) *RPCClient_BalanceAt_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Address), args[2].(*big.Int)) - }) - return _c -} - -func (_c *RPCClient_BalanceAt_Call) Return(_a0 *big.Int, _a1 error) *RPCClient_BalanceAt_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_BalanceAt_Call) RunAndReturn(run func(context.Context, common.Address, *big.Int) (*big.Int, error)) *RPCClient_BalanceAt_Call { - _c.Call.Return(run) - return _c -} - -// BatchCallContext provides a mock function with given fields: ctx, b -func (_m *RPCClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { - ret := _m.Called(ctx, b) - - if len(ret) == 0 { - panic("no return value specified for BatchCallContext") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, []rpc.BatchElem) error); ok { - r0 = rf(ctx, b) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// RPCClient_BatchCallContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BatchCallContext' -type RPCClient_BatchCallContext_Call struct { - *mock.Call -} - -// BatchCallContext is a helper method to define mock.On call -// - ctx context.Context -// - b []rpc.BatchElem -func (_e *RPCClient_Expecter) BatchCallContext(ctx interface{}, b interface{}) *RPCClient_BatchCallContext_Call { - return &RPCClient_BatchCallContext_Call{Call: _e.mock.On("BatchCallContext", ctx, b)} -} - -func (_c *RPCClient_BatchCallContext_Call) Run(run func(ctx context.Context, b []rpc.BatchElem)) *RPCClient_BatchCallContext_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]rpc.BatchElem)) - }) - return _c -} - -func (_c *RPCClient_BatchCallContext_Call) Return(_a0 error) *RPCClient_BatchCallContext_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *RPCClient_BatchCallContext_Call) RunAndReturn(run func(context.Context, []rpc.BatchElem) error) *RPCClient_BatchCallContext_Call { - _c.Call.Return(run) - return _c -} - -// BlockByHash provides a mock function with given fields: ctx, hash -func (_m *RPCClient) BlockByHash(ctx context.Context, hash common.Hash) (*types.Head, error) { - ret := _m.Called(ctx, hash) - - if len(ret) == 0 { - panic("no return value specified for BlockByHash") - } - - var r0 *types.Head - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Head, error)); ok { - return rf(ctx, hash) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *types.Head); ok { - r0 = rf(ctx, hash) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.Head) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { - r1 = rf(ctx, hash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_BlockByHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockByHash' -type RPCClient_BlockByHash_Call struct { - *mock.Call -} - -// BlockByHash is a helper method to define mock.On call -// - ctx context.Context -// - hash common.Hash -func (_e *RPCClient_Expecter) BlockByHash(ctx interface{}, hash interface{}) *RPCClient_BlockByHash_Call { - return &RPCClient_BlockByHash_Call{Call: _e.mock.On("BlockByHash", ctx, hash)} -} - -func (_c *RPCClient_BlockByHash_Call) Run(run func(ctx context.Context, hash common.Hash)) *RPCClient_BlockByHash_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Hash)) - }) - return _c -} - -func (_c *RPCClient_BlockByHash_Call) Return(_a0 *types.Head, _a1 error) *RPCClient_BlockByHash_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_BlockByHash_Call) RunAndReturn(run func(context.Context, common.Hash) (*types.Head, error)) *RPCClient_BlockByHash_Call { - _c.Call.Return(run) - return _c -} - -// BlockByHashGeth provides a mock function with given fields: ctx, hash -func (_m *RPCClient) BlockByHashGeth(ctx context.Context, hash common.Hash) (*coretypes.Block, error) { - ret := _m.Called(ctx, hash) - - if len(ret) == 0 { - panic("no return value specified for BlockByHashGeth") - } - - var r0 *coretypes.Block - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Block, error)); ok { - return rf(ctx, hash) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Block); ok { - r0 = rf(ctx, hash) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*coretypes.Block) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { - r1 = rf(ctx, hash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_BlockByHashGeth_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockByHashGeth' -type RPCClient_BlockByHashGeth_Call struct { - *mock.Call -} - -// BlockByHashGeth is a helper method to define mock.On call -// - ctx context.Context -// - hash common.Hash -func (_e *RPCClient_Expecter) BlockByHashGeth(ctx interface{}, hash interface{}) *RPCClient_BlockByHashGeth_Call { - return &RPCClient_BlockByHashGeth_Call{Call: _e.mock.On("BlockByHashGeth", ctx, hash)} -} - -func (_c *RPCClient_BlockByHashGeth_Call) Run(run func(ctx context.Context, hash common.Hash)) *RPCClient_BlockByHashGeth_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Hash)) - }) - return _c -} - -func (_c *RPCClient_BlockByHashGeth_Call) Return(b *coretypes.Block, err error) *RPCClient_BlockByHashGeth_Call { - _c.Call.Return(b, err) - return _c -} - -func (_c *RPCClient_BlockByHashGeth_Call) RunAndReturn(run func(context.Context, common.Hash) (*coretypes.Block, error)) *RPCClient_BlockByHashGeth_Call { - _c.Call.Return(run) - return _c -} - -// BlockByNumber provides a mock function with given fields: ctx, number -func (_m *RPCClient) BlockByNumber(ctx context.Context, number *big.Int) (*types.Head, error) { - ret := _m.Called(ctx, number) - - if len(ret) == 0 { - panic("no return value specified for BlockByNumber") - } - - var r0 *types.Head - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*types.Head, error)); ok { - return rf(ctx, number) - } - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *types.Head); ok { - r0 = rf(ctx, number) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.Head) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { - r1 = rf(ctx, number) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_BlockByNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockByNumber' -type RPCClient_BlockByNumber_Call struct { - *mock.Call -} - -// BlockByNumber is a helper method to define mock.On call -// - ctx context.Context -// - number *big.Int -func (_e *RPCClient_Expecter) BlockByNumber(ctx interface{}, number interface{}) *RPCClient_BlockByNumber_Call { - return &RPCClient_BlockByNumber_Call{Call: _e.mock.On("BlockByNumber", ctx, number)} -} - -func (_c *RPCClient_BlockByNumber_Call) Run(run func(ctx context.Context, number *big.Int)) *RPCClient_BlockByNumber_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*big.Int)) - }) - return _c -} - -func (_c *RPCClient_BlockByNumber_Call) Return(_a0 *types.Head, _a1 error) *RPCClient_BlockByNumber_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_BlockByNumber_Call) RunAndReturn(run func(context.Context, *big.Int) (*types.Head, error)) *RPCClient_BlockByNumber_Call { - _c.Call.Return(run) - return _c -} - -// BlockByNumberGeth provides a mock function with given fields: ctx, number -func (_m *RPCClient) BlockByNumberGeth(ctx context.Context, number *big.Int) (*coretypes.Block, error) { - ret := _m.Called(ctx, number) - - if len(ret) == 0 { - panic("no return value specified for BlockByNumberGeth") - } - - var r0 *coretypes.Block - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*coretypes.Block, error)); ok { - return rf(ctx, number) - } - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *coretypes.Block); ok { - r0 = rf(ctx, number) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*coretypes.Block) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { - r1 = rf(ctx, number) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_BlockByNumberGeth_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockByNumberGeth' -type RPCClient_BlockByNumberGeth_Call struct { - *mock.Call -} - -// BlockByNumberGeth is a helper method to define mock.On call -// - ctx context.Context -// - number *big.Int -func (_e *RPCClient_Expecter) BlockByNumberGeth(ctx interface{}, number interface{}) *RPCClient_BlockByNumberGeth_Call { - return &RPCClient_BlockByNumberGeth_Call{Call: _e.mock.On("BlockByNumberGeth", ctx, number)} -} - -func (_c *RPCClient_BlockByNumberGeth_Call) Run(run func(ctx context.Context, number *big.Int)) *RPCClient_BlockByNumberGeth_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*big.Int)) - }) - return _c -} - -func (_c *RPCClient_BlockByNumberGeth_Call) Return(b *coretypes.Block, err error) *RPCClient_BlockByNumberGeth_Call { - _c.Call.Return(b, err) - return _c -} - -func (_c *RPCClient_BlockByNumberGeth_Call) RunAndReturn(run func(context.Context, *big.Int) (*coretypes.Block, error)) *RPCClient_BlockByNumberGeth_Call { - _c.Call.Return(run) - return _c -} - -// CallContext provides a mock function with given fields: ctx, result, method, args -func (_m *RPCClient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { - var _ca []interface{} - _ca = append(_ca, ctx, result, method) - _ca = append(_ca, args...) - ret := _m.Called(_ca...) - - if len(ret) == 0 { - panic("no return value specified for CallContext") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, interface{}, string, ...interface{}) error); ok { - r0 = rf(ctx, result, method, args...) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// RPCClient_CallContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CallContext' -type RPCClient_CallContext_Call struct { - *mock.Call -} - -// CallContext is a helper method to define mock.On call -// - ctx context.Context -// - result interface{} -// - method string -// - args ...interface{} -func (_e *RPCClient_Expecter) CallContext(ctx interface{}, result interface{}, method interface{}, args ...interface{}) *RPCClient_CallContext_Call { - return &RPCClient_CallContext_Call{Call: _e.mock.On("CallContext", - append([]interface{}{ctx, result, method}, args...)...)} -} - -func (_c *RPCClient_CallContext_Call) Run(run func(ctx context.Context, result interface{}, method string, args ...interface{})) *RPCClient_CallContext_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-3) - for i, a := range args[3:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(args[0].(context.Context), args[1].(interface{}), args[2].(string), variadicArgs...) - }) - return _c -} - -func (_c *RPCClient_CallContext_Call) Return(_a0 error) *RPCClient_CallContext_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *RPCClient_CallContext_Call) RunAndReturn(run func(context.Context, interface{}, string, ...interface{}) error) *RPCClient_CallContext_Call { - _c.Call.Return(run) - return _c -} - -// CallContract provides a mock function with given fields: ctx, msg, blockNumber -func (_m *RPCClient) CallContract(ctx context.Context, msg interface{}, blockNumber *big.Int) ([]byte, error) { - ret := _m.Called(ctx, msg, blockNumber) - - if len(ret) == 0 { - panic("no return value specified for CallContract") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, interface{}, *big.Int) ([]byte, error)); ok { - return rf(ctx, msg, blockNumber) - } - if rf, ok := ret.Get(0).(func(context.Context, interface{}, *big.Int) []byte); ok { - r0 = rf(ctx, msg, blockNumber) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, interface{}, *big.Int) error); ok { - r1 = rf(ctx, msg, blockNumber) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_CallContract_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CallContract' -type RPCClient_CallContract_Call struct { - *mock.Call -} - -// CallContract is a helper method to define mock.On call -// - ctx context.Context -// - msg interface{} -// - blockNumber *big.Int -func (_e *RPCClient_Expecter) CallContract(ctx interface{}, msg interface{}, blockNumber interface{}) *RPCClient_CallContract_Call { - return &RPCClient_CallContract_Call{Call: _e.mock.On("CallContract", ctx, msg, blockNumber)} -} - -func (_c *RPCClient_CallContract_Call) Run(run func(ctx context.Context, msg interface{}, blockNumber *big.Int)) *RPCClient_CallContract_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(interface{}), args[2].(*big.Int)) - }) - return _c -} - -func (_c *RPCClient_CallContract_Call) Return(rpcErr []byte, extractErr error) *RPCClient_CallContract_Call { - _c.Call.Return(rpcErr, extractErr) - return _c -} - -func (_c *RPCClient_CallContract_Call) RunAndReturn(run func(context.Context, interface{}, *big.Int) ([]byte, error)) *RPCClient_CallContract_Call { - _c.Call.Return(run) - return _c -} - -// ChainID provides a mock function with given fields: ctx -func (_m *RPCClient) ChainID(ctx context.Context) (*big.Int, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for ChainID") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_ChainID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ChainID' -type RPCClient_ChainID_Call struct { - *mock.Call -} - -// ChainID is a helper method to define mock.On call -// - ctx context.Context -func (_e *RPCClient_Expecter) ChainID(ctx interface{}) *RPCClient_ChainID_Call { - return &RPCClient_ChainID_Call{Call: _e.mock.On("ChainID", ctx)} -} - -func (_c *RPCClient_ChainID_Call) Run(run func(ctx context.Context)) *RPCClient_ChainID_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *RPCClient_ChainID_Call) Return(_a0 *big.Int, _a1 error) *RPCClient_ChainID_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_ChainID_Call) RunAndReturn(run func(context.Context) (*big.Int, error)) *RPCClient_ChainID_Call { - _c.Call.Return(run) - return _c -} - -// ClientVersion provides a mock function with given fields: _a0 -func (_m *RPCClient) ClientVersion(_a0 context.Context) (string, error) { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for ClientVersion") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (string, error)); ok { - return rf(_a0) - } - if rf, ok := ret.Get(0).(func(context.Context) string); ok { - r0 = rf(_a0) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(_a0) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_ClientVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClientVersion' -type RPCClient_ClientVersion_Call struct { - *mock.Call -} - -// ClientVersion is a helper method to define mock.On call -// - _a0 context.Context -func (_e *RPCClient_Expecter) ClientVersion(_a0 interface{}) *RPCClient_ClientVersion_Call { - return &RPCClient_ClientVersion_Call{Call: _e.mock.On("ClientVersion", _a0)} -} - -func (_c *RPCClient_ClientVersion_Call) Run(run func(_a0 context.Context)) *RPCClient_ClientVersion_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *RPCClient_ClientVersion_Call) Return(_a0 string, _a1 error) *RPCClient_ClientVersion_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_ClientVersion_Call) RunAndReturn(run func(context.Context) (string, error)) *RPCClient_ClientVersion_Call { - _c.Call.Return(run) - return _c -} - -// Close provides a mock function with given fields: -func (_m *RPCClient) Close() { - _m.Called() -} - -// RPCClient_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type RPCClient_Close_Call struct { - *mock.Call -} - -// Close is a helper method to define mock.On call -func (_e *RPCClient_Expecter) Close() *RPCClient_Close_Call { - return &RPCClient_Close_Call{Call: _e.mock.On("Close")} -} - -func (_c *RPCClient_Close_Call) Run(run func()) *RPCClient_Close_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *RPCClient_Close_Call) Return() *RPCClient_Close_Call { - _c.Call.Return() - return _c -} - -func (_c *RPCClient_Close_Call) RunAndReturn(run func()) *RPCClient_Close_Call { - _c.Call.Return(run) - return _c -} - -// CodeAt provides a mock function with given fields: ctx, account, blockNumber -func (_m *RPCClient) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { - ret := _m.Called(ctx, account, blockNumber) - - if len(ret) == 0 { - panic("no return value specified for CodeAt") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) ([]byte, error)); ok { - return rf(ctx, account, blockNumber) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) []byte); ok { - r0 = rf(ctx, account, blockNumber) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Address, *big.Int) error); ok { - r1 = rf(ctx, account, blockNumber) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_CodeAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CodeAt' -type RPCClient_CodeAt_Call struct { - *mock.Call -} - -// CodeAt is a helper method to define mock.On call -// - ctx context.Context -// - account common.Address -// - blockNumber *big.Int -func (_e *RPCClient_Expecter) CodeAt(ctx interface{}, account interface{}, blockNumber interface{}) *RPCClient_CodeAt_Call { - return &RPCClient_CodeAt_Call{Call: _e.mock.On("CodeAt", ctx, account, blockNumber)} -} - -func (_c *RPCClient_CodeAt_Call) Run(run func(ctx context.Context, account common.Address, blockNumber *big.Int)) *RPCClient_CodeAt_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Address), args[2].(*big.Int)) - }) - return _c -} - -func (_c *RPCClient_CodeAt_Call) Return(_a0 []byte, _a1 error) *RPCClient_CodeAt_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_CodeAt_Call) RunAndReturn(run func(context.Context, common.Address, *big.Int) ([]byte, error)) *RPCClient_CodeAt_Call { - _c.Call.Return(run) - return _c -} - -// Dial provides a mock function with given fields: ctx -func (_m *RPCClient) Dial(ctx context.Context) error { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for Dial") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = rf(ctx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// RPCClient_Dial_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Dial' -type RPCClient_Dial_Call struct { - *mock.Call -} - -// Dial is a helper method to define mock.On call -// - ctx context.Context -func (_e *RPCClient_Expecter) Dial(ctx interface{}) *RPCClient_Dial_Call { - return &RPCClient_Dial_Call{Call: _e.mock.On("Dial", ctx)} -} - -func (_c *RPCClient_Dial_Call) Run(run func(ctx context.Context)) *RPCClient_Dial_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *RPCClient_Dial_Call) Return(_a0 error) *RPCClient_Dial_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *RPCClient_Dial_Call) RunAndReturn(run func(context.Context) error) *RPCClient_Dial_Call { - _c.Call.Return(run) - return _c -} - -// DialHTTP provides a mock function with given fields: -func (_m *RPCClient) DialHTTP() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for DialHTTP") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// RPCClient_DialHTTP_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DialHTTP' -type RPCClient_DialHTTP_Call struct { - *mock.Call -} - -// DialHTTP is a helper method to define mock.On call -func (_e *RPCClient_Expecter) DialHTTP() *RPCClient_DialHTTP_Call { - return &RPCClient_DialHTTP_Call{Call: _e.mock.On("DialHTTP")} -} - -func (_c *RPCClient_DialHTTP_Call) Run(run func()) *RPCClient_DialHTTP_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *RPCClient_DialHTTP_Call) Return(_a0 error) *RPCClient_DialHTTP_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *RPCClient_DialHTTP_Call) RunAndReturn(run func() error) *RPCClient_DialHTTP_Call { - _c.Call.Return(run) - return _c -} - -// DisconnectAll provides a mock function with given fields: -func (_m *RPCClient) DisconnectAll() { - _m.Called() -} - -// RPCClient_DisconnectAll_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectAll' -type RPCClient_DisconnectAll_Call struct { - *mock.Call -} - -// DisconnectAll is a helper method to define mock.On call -func (_e *RPCClient_Expecter) DisconnectAll() *RPCClient_DisconnectAll_Call { - return &RPCClient_DisconnectAll_Call{Call: _e.mock.On("DisconnectAll")} -} - -func (_c *RPCClient_DisconnectAll_Call) Run(run func()) *RPCClient_DisconnectAll_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *RPCClient_DisconnectAll_Call) Return() *RPCClient_DisconnectAll_Call { - _c.Call.Return() - return _c -} - -func (_c *RPCClient_DisconnectAll_Call) RunAndReturn(run func()) *RPCClient_DisconnectAll_Call { - _c.Call.Return(run) - return _c -} - -// EstimateGas provides a mock function with given fields: ctx, call -func (_m *RPCClient) EstimateGas(ctx context.Context, call interface{}) (uint64, error) { - ret := _m.Called(ctx, call) - - if len(ret) == 0 { - panic("no return value specified for EstimateGas") - } - - var r0 uint64 - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, interface{}) (uint64, error)); ok { - return rf(ctx, call) - } - if rf, ok := ret.Get(0).(func(context.Context, interface{}) uint64); ok { - r0 = rf(ctx, call) - } else { - r0 = ret.Get(0).(uint64) - } - - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, call) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_EstimateGas_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EstimateGas' -type RPCClient_EstimateGas_Call struct { - *mock.Call -} - -// EstimateGas is a helper method to define mock.On call -// - ctx context.Context -// - call interface{} -func (_e *RPCClient_Expecter) EstimateGas(ctx interface{}, call interface{}) *RPCClient_EstimateGas_Call { - return &RPCClient_EstimateGas_Call{Call: _e.mock.On("EstimateGas", ctx, call)} -} - -func (_c *RPCClient_EstimateGas_Call) Run(run func(ctx context.Context, call interface{})) *RPCClient_EstimateGas_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(interface{})) - }) - return _c -} - -func (_c *RPCClient_EstimateGas_Call) Return(gas uint64, err error) *RPCClient_EstimateGas_Call { - _c.Call.Return(gas, err) - return _c -} - -func (_c *RPCClient_EstimateGas_Call) RunAndReturn(run func(context.Context, interface{}) (uint64, error)) *RPCClient_EstimateGas_Call { - _c.Call.Return(run) - return _c -} - -// FeeHistory provides a mock function with given fields: ctx, blockCount, rewardPercentiles -func (_m *RPCClient) FeeHistory(ctx context.Context, blockCount uint64, rewardPercentiles []float64) (*ethereum.FeeHistory, error) { - ret := _m.Called(ctx, blockCount, rewardPercentiles) - - if len(ret) == 0 { - panic("no return value specified for FeeHistory") - } - - var r0 *ethereum.FeeHistory - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, []float64) (*ethereum.FeeHistory, error)); ok { - return rf(ctx, blockCount, rewardPercentiles) - } - if rf, ok := ret.Get(0).(func(context.Context, uint64, []float64) *ethereum.FeeHistory); ok { - r0 = rf(ctx, blockCount, rewardPercentiles) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*ethereum.FeeHistory) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, uint64, []float64) error); ok { - r1 = rf(ctx, blockCount, rewardPercentiles) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_FeeHistory_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FeeHistory' -type RPCClient_FeeHistory_Call struct { - *mock.Call -} - -// FeeHistory is a helper method to define mock.On call -// - ctx context.Context -// - blockCount uint64 -// - rewardPercentiles []float64 -func (_e *RPCClient_Expecter) FeeHistory(ctx interface{}, blockCount interface{}, rewardPercentiles interface{}) *RPCClient_FeeHistory_Call { - return &RPCClient_FeeHistory_Call{Call: _e.mock.On("FeeHistory", ctx, blockCount, rewardPercentiles)} -} - -func (_c *RPCClient_FeeHistory_Call) Run(run func(ctx context.Context, blockCount uint64, rewardPercentiles []float64)) *RPCClient_FeeHistory_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint64), args[2].([]float64)) - }) - return _c -} - -func (_c *RPCClient_FeeHistory_Call) Return(feeHistory *ethereum.FeeHistory, err error) *RPCClient_FeeHistory_Call { - _c.Call.Return(feeHistory, err) - return _c -} - -func (_c *RPCClient_FeeHistory_Call) RunAndReturn(run func(context.Context, uint64, []float64) (*ethereum.FeeHistory, error)) *RPCClient_FeeHistory_Call { - _c.Call.Return(run) - return _c -} - -// FilterEvents provides a mock function with given fields: ctx, query -func (_m *RPCClient) FilterEvents(ctx context.Context, query ethereum.FilterQuery) ([]coretypes.Log, error) { - ret := _m.Called(ctx, query) - - if len(ret) == 0 { - panic("no return value specified for FilterEvents") - } - - var r0 []coretypes.Log - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery) ([]coretypes.Log, error)); ok { - return rf(ctx, query) - } - if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery) []coretypes.Log); ok { - r0 = rf(ctx, query) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]coretypes.Log) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, ethereum.FilterQuery) error); ok { - r1 = rf(ctx, query) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_FilterEvents_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FilterEvents' -type RPCClient_FilterEvents_Call struct { - *mock.Call -} - -// FilterEvents is a helper method to define mock.On call -// - ctx context.Context -// - query ethereum.FilterQuery -func (_e *RPCClient_Expecter) FilterEvents(ctx interface{}, query interface{}) *RPCClient_FilterEvents_Call { - return &RPCClient_FilterEvents_Call{Call: _e.mock.On("FilterEvents", ctx, query)} -} - -func (_c *RPCClient_FilterEvents_Call) Run(run func(ctx context.Context, query ethereum.FilterQuery)) *RPCClient_FilterEvents_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(ethereum.FilterQuery)) - }) - return _c -} - -func (_c *RPCClient_FilterEvents_Call) Return(_a0 []coretypes.Log, _a1 error) *RPCClient_FilterEvents_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_FilterEvents_Call) RunAndReturn(run func(context.Context, ethereum.FilterQuery) ([]coretypes.Log, error)) *RPCClient_FilterEvents_Call { - _c.Call.Return(run) - return _c -} - -// GetInterceptedChainInfo provides a mock function with given fields: -func (_m *RPCClient) GetInterceptedChainInfo() (commonclient.ChainInfo, commonclient.ChainInfo) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetInterceptedChainInfo") - } - - var r0 commonclient.ChainInfo - var r1 commonclient.ChainInfo - if rf, ok := ret.Get(0).(func() (commonclient.ChainInfo, commonclient.ChainInfo)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() commonclient.ChainInfo); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(commonclient.ChainInfo) - } - - if rf, ok := ret.Get(1).(func() commonclient.ChainInfo); ok { - r1 = rf() - } else { - r1 = ret.Get(1).(commonclient.ChainInfo) - } - - return r0, r1 -} - -// RPCClient_GetInterceptedChainInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetInterceptedChainInfo' -type RPCClient_GetInterceptedChainInfo_Call struct { - *mock.Call -} - -// GetInterceptedChainInfo is a helper method to define mock.On call -func (_e *RPCClient_Expecter) GetInterceptedChainInfo() *RPCClient_GetInterceptedChainInfo_Call { - return &RPCClient_GetInterceptedChainInfo_Call{Call: _e.mock.On("GetInterceptedChainInfo")} -} - -func (_c *RPCClient_GetInterceptedChainInfo_Call) Run(run func()) *RPCClient_GetInterceptedChainInfo_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *RPCClient_GetInterceptedChainInfo_Call) Return(latest commonclient.ChainInfo, highestUserObservations commonclient.ChainInfo) *RPCClient_GetInterceptedChainInfo_Call { - _c.Call.Return(latest, highestUserObservations) - return _c -} - -func (_c *RPCClient_GetInterceptedChainInfo_Call) RunAndReturn(run func() (commonclient.ChainInfo, commonclient.ChainInfo)) *RPCClient_GetInterceptedChainInfo_Call { - _c.Call.Return(run) - return _c -} - -// HeaderByHash provides a mock function with given fields: ctx, h -func (_m *RPCClient) HeaderByHash(ctx context.Context, h common.Hash) (*coretypes.Header, error) { - ret := _m.Called(ctx, h) - - if len(ret) == 0 { - panic("no return value specified for HeaderByHash") - } - - var r0 *coretypes.Header - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Header, error)); ok { - return rf(ctx, h) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Header); ok { - r0 = rf(ctx, h) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*coretypes.Header) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { - r1 = rf(ctx, h) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_HeaderByHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HeaderByHash' -type RPCClient_HeaderByHash_Call struct { - *mock.Call -} - -// HeaderByHash is a helper method to define mock.On call -// - ctx context.Context -// - h common.Hash -func (_e *RPCClient_Expecter) HeaderByHash(ctx interface{}, h interface{}) *RPCClient_HeaderByHash_Call { - return &RPCClient_HeaderByHash_Call{Call: _e.mock.On("HeaderByHash", ctx, h)} -} - -func (_c *RPCClient_HeaderByHash_Call) Run(run func(ctx context.Context, h common.Hash)) *RPCClient_HeaderByHash_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Hash)) - }) - return _c -} - -func (_c *RPCClient_HeaderByHash_Call) Return(head *coretypes.Header, err error) *RPCClient_HeaderByHash_Call { - _c.Call.Return(head, err) - return _c -} - -func (_c *RPCClient_HeaderByHash_Call) RunAndReturn(run func(context.Context, common.Hash) (*coretypes.Header, error)) *RPCClient_HeaderByHash_Call { - _c.Call.Return(run) - return _c -} - -// HeaderByNumber provides a mock function with given fields: ctx, n -func (_m *RPCClient) HeaderByNumber(ctx context.Context, n *big.Int) (*coretypes.Header, error) { - ret := _m.Called(ctx, n) - - if len(ret) == 0 { - panic("no return value specified for HeaderByNumber") - } - - var r0 *coretypes.Header - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*coretypes.Header, error)); ok { - return rf(ctx, n) - } - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *coretypes.Header); ok { - r0 = rf(ctx, n) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*coretypes.Header) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { - r1 = rf(ctx, n) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_HeaderByNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HeaderByNumber' -type RPCClient_HeaderByNumber_Call struct { - *mock.Call -} - -// HeaderByNumber is a helper method to define mock.On call -// - ctx context.Context -// - n *big.Int -func (_e *RPCClient_Expecter) HeaderByNumber(ctx interface{}, n interface{}) *RPCClient_HeaderByNumber_Call { - return &RPCClient_HeaderByNumber_Call{Call: _e.mock.On("HeaderByNumber", ctx, n)} -} - -func (_c *RPCClient_HeaderByNumber_Call) Run(run func(ctx context.Context, n *big.Int)) *RPCClient_HeaderByNumber_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*big.Int)) - }) - return _c -} - -func (_c *RPCClient_HeaderByNumber_Call) Return(head *coretypes.Header, err error) *RPCClient_HeaderByNumber_Call { - _c.Call.Return(head, err) - return _c -} - -func (_c *RPCClient_HeaderByNumber_Call) RunAndReturn(run func(context.Context, *big.Int) (*coretypes.Header, error)) *RPCClient_HeaderByNumber_Call { - _c.Call.Return(run) - return _c -} - -// IsSyncing provides a mock function with given fields: ctx -func (_m *RPCClient) IsSyncing(ctx context.Context) (bool, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for IsSyncing") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (bool, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) bool); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_IsSyncing_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsSyncing' -type RPCClient_IsSyncing_Call struct { - *mock.Call -} - -// IsSyncing is a helper method to define mock.On call -// - ctx context.Context -func (_e *RPCClient_Expecter) IsSyncing(ctx interface{}) *RPCClient_IsSyncing_Call { - return &RPCClient_IsSyncing_Call{Call: _e.mock.On("IsSyncing", ctx)} -} - -func (_c *RPCClient_IsSyncing_Call) Run(run func(ctx context.Context)) *RPCClient_IsSyncing_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *RPCClient_IsSyncing_Call) Return(_a0 bool, _a1 error) *RPCClient_IsSyncing_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_IsSyncing_Call) RunAndReturn(run func(context.Context) (bool, error)) *RPCClient_IsSyncing_Call { - _c.Call.Return(run) - return _c -} - -// LINKBalance provides a mock function with given fields: ctx, accountAddress, linkAddress -func (_m *RPCClient) LINKBalance(ctx context.Context, accountAddress common.Address, linkAddress common.Address) (*assets.Link, error) { - ret := _m.Called(ctx, accountAddress, linkAddress) - - if len(ret) == 0 { - panic("no return value specified for LINKBalance") - } - - var r0 *assets.Link - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Address) (*assets.Link, error)); ok { - return rf(ctx, accountAddress, linkAddress) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Address) *assets.Link); ok { - r0 = rf(ctx, accountAddress, linkAddress) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*assets.Link) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Address, common.Address) error); ok { - r1 = rf(ctx, accountAddress, linkAddress) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_LINKBalance_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LINKBalance' -type RPCClient_LINKBalance_Call struct { - *mock.Call -} - -// LINKBalance is a helper method to define mock.On call -// - ctx context.Context -// - accountAddress common.Address -// - linkAddress common.Address -func (_e *RPCClient_Expecter) LINKBalance(ctx interface{}, accountAddress interface{}, linkAddress interface{}) *RPCClient_LINKBalance_Call { - return &RPCClient_LINKBalance_Call{Call: _e.mock.On("LINKBalance", ctx, accountAddress, linkAddress)} -} - -func (_c *RPCClient_LINKBalance_Call) Run(run func(ctx context.Context, accountAddress common.Address, linkAddress common.Address)) *RPCClient_LINKBalance_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Address), args[2].(common.Address)) - }) - return _c -} - -func (_c *RPCClient_LINKBalance_Call) Return(_a0 *assets.Link, _a1 error) *RPCClient_LINKBalance_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_LINKBalance_Call) RunAndReturn(run func(context.Context, common.Address, common.Address) (*assets.Link, error)) *RPCClient_LINKBalance_Call { - _c.Call.Return(run) - return _c -} - -// LatestBlockHeight provides a mock function with given fields: _a0 -func (_m *RPCClient) LatestBlockHeight(_a0 context.Context) (*big.Int, error) { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for LatestBlockHeight") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { - return rf(_a0) - } - if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { - r0 = rf(_a0) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(_a0) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_LatestBlockHeight_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LatestBlockHeight' -type RPCClient_LatestBlockHeight_Call struct { - *mock.Call -} - -// LatestBlockHeight is a helper method to define mock.On call -// - _a0 context.Context -func (_e *RPCClient_Expecter) LatestBlockHeight(_a0 interface{}) *RPCClient_LatestBlockHeight_Call { - return &RPCClient_LatestBlockHeight_Call{Call: _e.mock.On("LatestBlockHeight", _a0)} -} - -func (_c *RPCClient_LatestBlockHeight_Call) Run(run func(_a0 context.Context)) *RPCClient_LatestBlockHeight_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *RPCClient_LatestBlockHeight_Call) Return(_a0 *big.Int, _a1 error) *RPCClient_LatestBlockHeight_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_LatestBlockHeight_Call) RunAndReturn(run func(context.Context) (*big.Int, error)) *RPCClient_LatestBlockHeight_Call { - _c.Call.Return(run) - return _c -} - -// LatestFinalizedBlock provides a mock function with given fields: ctx -func (_m *RPCClient) LatestFinalizedBlock(ctx context.Context) (*types.Head, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for LatestFinalizedBlock") - } - - var r0 *types.Head - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (*types.Head, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) *types.Head); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.Head) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_LatestFinalizedBlock_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LatestFinalizedBlock' -type RPCClient_LatestFinalizedBlock_Call struct { - *mock.Call -} - -// LatestFinalizedBlock is a helper method to define mock.On call -// - ctx context.Context -func (_e *RPCClient_Expecter) LatestFinalizedBlock(ctx interface{}) *RPCClient_LatestFinalizedBlock_Call { - return &RPCClient_LatestFinalizedBlock_Call{Call: _e.mock.On("LatestFinalizedBlock", ctx)} -} - -func (_c *RPCClient_LatestFinalizedBlock_Call) Run(run func(ctx context.Context)) *RPCClient_LatestFinalizedBlock_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *RPCClient_LatestFinalizedBlock_Call) Return(_a0 *types.Head, _a1 error) *RPCClient_LatestFinalizedBlock_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_LatestFinalizedBlock_Call) RunAndReturn(run func(context.Context) (*types.Head, error)) *RPCClient_LatestFinalizedBlock_Call { - _c.Call.Return(run) - return _c -} - -// PendingCallContract provides a mock function with given fields: ctx, msg -func (_m *RPCClient) PendingCallContract(ctx context.Context, msg interface{}) ([]byte, error) { - ret := _m.Called(ctx, msg) - - if len(ret) == 0 { - panic("no return value specified for PendingCallContract") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, interface{}) ([]byte, error)); ok { - return rf(ctx, msg) - } - if rf, ok := ret.Get(0).(func(context.Context, interface{}) []byte); ok { - r0 = rf(ctx, msg) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, msg) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_PendingCallContract_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PendingCallContract' -type RPCClient_PendingCallContract_Call struct { - *mock.Call -} - -// PendingCallContract is a helper method to define mock.On call -// - ctx context.Context -// - msg interface{} -func (_e *RPCClient_Expecter) PendingCallContract(ctx interface{}, msg interface{}) *RPCClient_PendingCallContract_Call { - return &RPCClient_PendingCallContract_Call{Call: _e.mock.On("PendingCallContract", ctx, msg)} -} - -func (_c *RPCClient_PendingCallContract_Call) Run(run func(ctx context.Context, msg interface{})) *RPCClient_PendingCallContract_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(interface{})) - }) - return _c -} - -func (_c *RPCClient_PendingCallContract_Call) Return(rpcErr []byte, extractErr error) *RPCClient_PendingCallContract_Call { - _c.Call.Return(rpcErr, extractErr) - return _c -} - -func (_c *RPCClient_PendingCallContract_Call) RunAndReturn(run func(context.Context, interface{}) ([]byte, error)) *RPCClient_PendingCallContract_Call { - _c.Call.Return(run) - return _c -} - -// PendingCodeAt provides a mock function with given fields: ctx, account -func (_m *RPCClient) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { - ret := _m.Called(ctx, account) - - if len(ret) == 0 { - panic("no return value specified for PendingCodeAt") - } - - var r0 []byte - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address) ([]byte, error)); ok { - return rf(ctx, account) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Address) []byte); ok { - r0 = rf(ctx, account) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Address) error); ok { - r1 = rf(ctx, account) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_PendingCodeAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PendingCodeAt' -type RPCClient_PendingCodeAt_Call struct { - *mock.Call -} - -// PendingCodeAt is a helper method to define mock.On call -// - ctx context.Context -// - account common.Address -func (_e *RPCClient_Expecter) PendingCodeAt(ctx interface{}, account interface{}) *RPCClient_PendingCodeAt_Call { - return &RPCClient_PendingCodeAt_Call{Call: _e.mock.On("PendingCodeAt", ctx, account)} -} - -func (_c *RPCClient_PendingCodeAt_Call) Run(run func(ctx context.Context, account common.Address)) *RPCClient_PendingCodeAt_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Address)) - }) - return _c -} - -func (_c *RPCClient_PendingCodeAt_Call) Return(b []byte, err error) *RPCClient_PendingCodeAt_Call { - _c.Call.Return(b, err) - return _c -} - -func (_c *RPCClient_PendingCodeAt_Call) RunAndReturn(run func(context.Context, common.Address) ([]byte, error)) *RPCClient_PendingCodeAt_Call { - _c.Call.Return(run) - return _c -} - -// PendingSequenceAt provides a mock function with given fields: ctx, addr -func (_m *RPCClient) PendingSequenceAt(ctx context.Context, addr common.Address) (types.Nonce, error) { - ret := _m.Called(ctx, addr) - - if len(ret) == 0 { - panic("no return value specified for PendingSequenceAt") - } - - var r0 types.Nonce - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address) (types.Nonce, error)); ok { - return rf(ctx, addr) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Address) types.Nonce); ok { - r0 = rf(ctx, addr) - } else { - r0 = ret.Get(0).(types.Nonce) - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Address) error); ok { - r1 = rf(ctx, addr) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_PendingSequenceAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PendingSequenceAt' -type RPCClient_PendingSequenceAt_Call struct { - *mock.Call -} - -// PendingSequenceAt is a helper method to define mock.On call -// - ctx context.Context -// - addr common.Address -func (_e *RPCClient_Expecter) PendingSequenceAt(ctx interface{}, addr interface{}) *RPCClient_PendingSequenceAt_Call { - return &RPCClient_PendingSequenceAt_Call{Call: _e.mock.On("PendingSequenceAt", ctx, addr)} -} - -func (_c *RPCClient_PendingSequenceAt_Call) Run(run func(ctx context.Context, addr common.Address)) *RPCClient_PendingSequenceAt_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Address)) - }) - return _c -} - -func (_c *RPCClient_PendingSequenceAt_Call) Return(_a0 types.Nonce, _a1 error) *RPCClient_PendingSequenceAt_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_PendingSequenceAt_Call) RunAndReturn(run func(context.Context, common.Address) (types.Nonce, error)) *RPCClient_PendingSequenceAt_Call { - _c.Call.Return(run) - return _c -} - -// SendEmptyTransaction provides a mock function with given fields: ctx, newTxAttempt, seq, gasLimit, fee, fromAddress -func (_m *RPCClient) SendEmptyTransaction(ctx context.Context, newTxAttempt func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), seq types.Nonce, gasLimit uint32, fee *evmassets.Wei, fromAddress common.Address) (string, error) { - ret := _m.Called(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) - - if len(ret) == 0 { - panic("no return value specified for SendEmptyTransaction") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), types.Nonce, uint32, *evmassets.Wei, common.Address) (string, error)); ok { - return rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) - } - if rf, ok := ret.Get(0).(func(context.Context, func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), types.Nonce, uint32, *evmassets.Wei, common.Address) string); ok { - r0 = rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(context.Context, func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), types.Nonce, uint32, *evmassets.Wei, common.Address) error); ok { - r1 = rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_SendEmptyTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendEmptyTransaction' -type RPCClient_SendEmptyTransaction_Call struct { - *mock.Call -} - -// SendEmptyTransaction is a helper method to define mock.On call -// - ctx context.Context -// - newTxAttempt func(types.Nonce , uint32 , *evmassets.Wei , common.Address)(interface{} , error) -// - seq types.Nonce -// - gasLimit uint32 -// - fee *evmassets.Wei -// - fromAddress common.Address -func (_e *RPCClient_Expecter) SendEmptyTransaction(ctx interface{}, newTxAttempt interface{}, seq interface{}, gasLimit interface{}, fee interface{}, fromAddress interface{}) *RPCClient_SendEmptyTransaction_Call { - return &RPCClient_SendEmptyTransaction_Call{Call: _e.mock.On("SendEmptyTransaction", ctx, newTxAttempt, seq, gasLimit, fee, fromAddress)} -} - -func (_c *RPCClient_SendEmptyTransaction_Call) Run(run func(ctx context.Context, newTxAttempt func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), seq types.Nonce, gasLimit uint32, fee *evmassets.Wei, fromAddress common.Address)) *RPCClient_SendEmptyTransaction_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error)), args[2].(types.Nonce), args[3].(uint32), args[4].(*evmassets.Wei), args[5].(common.Address)) - }) - return _c -} - -func (_c *RPCClient_SendEmptyTransaction_Call) Return(txhash string, err error) *RPCClient_SendEmptyTransaction_Call { - _c.Call.Return(txhash, err) - return _c -} - -func (_c *RPCClient_SendEmptyTransaction_Call) RunAndReturn(run func(context.Context, func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), types.Nonce, uint32, *evmassets.Wei, common.Address) (string, error)) *RPCClient_SendEmptyTransaction_Call { - _c.Call.Return(run) - return _c -} - -// SendTransaction provides a mock function with given fields: ctx, tx -func (_m *RPCClient) SendTransaction(ctx context.Context, tx *coretypes.Transaction) error { - ret := _m.Called(ctx, tx) - - if len(ret) == 0 { - panic("no return value specified for SendTransaction") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *coretypes.Transaction) error); ok { - r0 = rf(ctx, tx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// RPCClient_SendTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendTransaction' -type RPCClient_SendTransaction_Call struct { - *mock.Call -} - -// SendTransaction is a helper method to define mock.On call -// - ctx context.Context -// - tx *coretypes.Transaction -func (_e *RPCClient_Expecter) SendTransaction(ctx interface{}, tx interface{}) *RPCClient_SendTransaction_Call { - return &RPCClient_SendTransaction_Call{Call: _e.mock.On("SendTransaction", ctx, tx)} -} - -func (_c *RPCClient_SendTransaction_Call) Run(run func(ctx context.Context, tx *coretypes.Transaction)) *RPCClient_SendTransaction_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*coretypes.Transaction)) - }) - return _c -} - -func (_c *RPCClient_SendTransaction_Call) Return(_a0 error) *RPCClient_SendTransaction_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *RPCClient_SendTransaction_Call) RunAndReturn(run func(context.Context, *coretypes.Transaction) error) *RPCClient_SendTransaction_Call { - _c.Call.Return(run) - return _c -} - -// SequenceAt provides a mock function with given fields: ctx, accountAddress, blockNumber -func (_m *RPCClient) SequenceAt(ctx context.Context, accountAddress common.Address, blockNumber *big.Int) (types.Nonce, error) { - ret := _m.Called(ctx, accountAddress, blockNumber) - - if len(ret) == 0 { - panic("no return value specified for SequenceAt") - } - - var r0 types.Nonce - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) (types.Nonce, error)); ok { - return rf(ctx, accountAddress, blockNumber) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) types.Nonce); ok { - r0 = rf(ctx, accountAddress, blockNumber) - } else { - r0 = ret.Get(0).(types.Nonce) - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Address, *big.Int) error); ok { - r1 = rf(ctx, accountAddress, blockNumber) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_SequenceAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SequenceAt' -type RPCClient_SequenceAt_Call struct { - *mock.Call -} - -// SequenceAt is a helper method to define mock.On call -// - ctx context.Context -// - accountAddress common.Address -// - blockNumber *big.Int -func (_e *RPCClient_Expecter) SequenceAt(ctx interface{}, accountAddress interface{}, blockNumber interface{}) *RPCClient_SequenceAt_Call { - return &RPCClient_SequenceAt_Call{Call: _e.mock.On("SequenceAt", ctx, accountAddress, blockNumber)} -} - -func (_c *RPCClient_SequenceAt_Call) Run(run func(ctx context.Context, accountAddress common.Address, blockNumber *big.Int)) *RPCClient_SequenceAt_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Address), args[2].(*big.Int)) - }) - return _c -} - -func (_c *RPCClient_SequenceAt_Call) Return(_a0 types.Nonce, _a1 error) *RPCClient_SequenceAt_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_SequenceAt_Call) RunAndReturn(run func(context.Context, common.Address, *big.Int) (types.Nonce, error)) *RPCClient_SequenceAt_Call { - _c.Call.Return(run) - return _c -} - -// SetAliveLoopSub provides a mock function with given fields: _a0 -func (_m *RPCClient) SetAliveLoopSub(_a0 commontypes.Subscription) { - _m.Called(_a0) -} - -// RPCClient_SetAliveLoopSub_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetAliveLoopSub' -type RPCClient_SetAliveLoopSub_Call struct { - *mock.Call -} - -// SetAliveLoopSub is a helper method to define mock.On call -// - _a0 commontypes.Subscription -func (_e *RPCClient_Expecter) SetAliveLoopSub(_a0 interface{}) *RPCClient_SetAliveLoopSub_Call { - return &RPCClient_SetAliveLoopSub_Call{Call: _e.mock.On("SetAliveLoopSub", _a0)} -} - -func (_c *RPCClient_SetAliveLoopSub_Call) Run(run func(_a0 commontypes.Subscription)) *RPCClient_SetAliveLoopSub_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(commontypes.Subscription)) - }) - return _c -} - -func (_c *RPCClient_SetAliveLoopSub_Call) Return() *RPCClient_SetAliveLoopSub_Call { - _c.Call.Return() - return _c -} - -func (_c *RPCClient_SetAliveLoopSub_Call) RunAndReturn(run func(commontypes.Subscription)) *RPCClient_SetAliveLoopSub_Call { - _c.Call.Return(run) - return _c -} - -// SimulateTransaction provides a mock function with given fields: ctx, tx -func (_m *RPCClient) SimulateTransaction(ctx context.Context, tx *coretypes.Transaction) error { - ret := _m.Called(ctx, tx) - - if len(ret) == 0 { - panic("no return value specified for SimulateTransaction") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *coretypes.Transaction) error); ok { - r0 = rf(ctx, tx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// RPCClient_SimulateTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SimulateTransaction' -type RPCClient_SimulateTransaction_Call struct { - *mock.Call -} - -// SimulateTransaction is a helper method to define mock.On call -// - ctx context.Context -// - tx *coretypes.Transaction -func (_e *RPCClient_Expecter) SimulateTransaction(ctx interface{}, tx interface{}) *RPCClient_SimulateTransaction_Call { - return &RPCClient_SimulateTransaction_Call{Call: _e.mock.On("SimulateTransaction", ctx, tx)} -} - -func (_c *RPCClient_SimulateTransaction_Call) Run(run func(ctx context.Context, tx *coretypes.Transaction)) *RPCClient_SimulateTransaction_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*coretypes.Transaction)) - }) - return _c -} - -func (_c *RPCClient_SimulateTransaction_Call) Return(_a0 error) *RPCClient_SimulateTransaction_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *RPCClient_SimulateTransaction_Call) RunAndReturn(run func(context.Context, *coretypes.Transaction) error) *RPCClient_SimulateTransaction_Call { - _c.Call.Return(run) - return _c -} - -// SubscribeFilterLogs provides a mock function with given fields: ctx, q, ch -func (_m *RPCClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- coretypes.Log) (ethereum.Subscription, error) { - ret := _m.Called(ctx, q, ch) - - if len(ret) == 0 { - panic("no return value specified for SubscribeFilterLogs") - } - - var r0 ethereum.Subscription - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) (ethereum.Subscription, error)); ok { - return rf(ctx, q, ch) - } - if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) ethereum.Subscription); ok { - r0 = rf(ctx, q, ch) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(ethereum.Subscription) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) error); ok { - r1 = rf(ctx, q, ch) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_SubscribeFilterLogs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeFilterLogs' -type RPCClient_SubscribeFilterLogs_Call struct { - *mock.Call -} - -// SubscribeFilterLogs is a helper method to define mock.On call -// - ctx context.Context -// - q ethereum.FilterQuery -// - ch chan<- coretypes.Log -func (_e *RPCClient_Expecter) SubscribeFilterLogs(ctx interface{}, q interface{}, ch interface{}) *RPCClient_SubscribeFilterLogs_Call { - return &RPCClient_SubscribeFilterLogs_Call{Call: _e.mock.On("SubscribeFilterLogs", ctx, q, ch)} -} - -func (_c *RPCClient_SubscribeFilterLogs_Call) Run(run func(ctx context.Context, q ethereum.FilterQuery, ch chan<- coretypes.Log)) *RPCClient_SubscribeFilterLogs_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(ethereum.FilterQuery), args[2].(chan<- coretypes.Log)) - }) - return _c -} - -func (_c *RPCClient_SubscribeFilterLogs_Call) Return(s ethereum.Subscription, err error) *RPCClient_SubscribeFilterLogs_Call { - _c.Call.Return(s, err) - return _c -} - -func (_c *RPCClient_SubscribeFilterLogs_Call) RunAndReturn(run func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) (ethereum.Subscription, error)) *RPCClient_SubscribeFilterLogs_Call { - _c.Call.Return(run) - return _c -} - -// SubscribeNewHead provides a mock function with given fields: ctx, channel -func (_m *RPCClient) SubscribeNewHead(ctx context.Context, channel chan<- *types.Head) (commontypes.Subscription, error) { - ret := _m.Called(ctx, channel) - - if len(ret) == 0 { - panic("no return value specified for SubscribeNewHead") - } - - var r0 commontypes.Subscription - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, chan<- *types.Head) (commontypes.Subscription, error)); ok { - return rf(ctx, channel) - } - if rf, ok := ret.Get(0).(func(context.Context, chan<- *types.Head) commontypes.Subscription); ok { - r0 = rf(ctx, channel) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(commontypes.Subscription) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, chan<- *types.Head) error); ok { - r1 = rf(ctx, channel) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_SubscribeNewHead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeNewHead' -type RPCClient_SubscribeNewHead_Call struct { - *mock.Call -} - -// SubscribeNewHead is a helper method to define mock.On call -// - ctx context.Context -// - channel chan<- *types.Head -func (_e *RPCClient_Expecter) SubscribeNewHead(ctx interface{}, channel interface{}) *RPCClient_SubscribeNewHead_Call { - return &RPCClient_SubscribeNewHead_Call{Call: _e.mock.On("SubscribeNewHead", ctx, channel)} -} - -func (_c *RPCClient_SubscribeNewHead_Call) Run(run func(ctx context.Context, channel chan<- *types.Head)) *RPCClient_SubscribeNewHead_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(chan<- *types.Head)) - }) - return _c -} - -func (_c *RPCClient_SubscribeNewHead_Call) Return(s commontypes.Subscription, err error) *RPCClient_SubscribeNewHead_Call { - _c.Call.Return(s, err) - return _c -} - -func (_c *RPCClient_SubscribeNewHead_Call) RunAndReturn(run func(context.Context, chan<- *types.Head) (commontypes.Subscription, error)) *RPCClient_SubscribeNewHead_Call { - _c.Call.Return(run) - return _c -} - -// SubscribeToFinalizedHeads provides a mock function with given fields: _a0 -func (_m *RPCClient) SubscribeToFinalizedHeads(_a0 context.Context) (<-chan *types.Head, commontypes.Subscription, error) { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for SubscribeToFinalizedHeads") - } - - var r0 <-chan *types.Head - var r1 commontypes.Subscription - var r2 error - if rf, ok := ret.Get(0).(func(context.Context) (<-chan *types.Head, commontypes.Subscription, error)); ok { - return rf(_a0) - } - if rf, ok := ret.Get(0).(func(context.Context) <-chan *types.Head); ok { - r0 = rf(_a0) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan *types.Head) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) commontypes.Subscription); ok { - r1 = rf(_a0) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(commontypes.Subscription) - } - } - - if rf, ok := ret.Get(2).(func(context.Context) error); ok { - r2 = rf(_a0) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// RPCClient_SubscribeToFinalizedHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToFinalizedHeads' -type RPCClient_SubscribeToFinalizedHeads_Call struct { - *mock.Call -} - -// SubscribeToFinalizedHeads is a helper method to define mock.On call -// - _a0 context.Context -func (_e *RPCClient_Expecter) SubscribeToFinalizedHeads(_a0 interface{}) *RPCClient_SubscribeToFinalizedHeads_Call { - return &RPCClient_SubscribeToFinalizedHeads_Call{Call: _e.mock.On("SubscribeToFinalizedHeads", _a0)} -} - -func (_c *RPCClient_SubscribeToFinalizedHeads_Call) Run(run func(_a0 context.Context)) *RPCClient_SubscribeToFinalizedHeads_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *RPCClient_SubscribeToFinalizedHeads_Call) Return(_a0 <-chan *types.Head, _a1 commontypes.Subscription, _a2 error) *RPCClient_SubscribeToFinalizedHeads_Call { - _c.Call.Return(_a0, _a1, _a2) - return _c -} - -func (_c *RPCClient_SubscribeToFinalizedHeads_Call) RunAndReturn(run func(context.Context) (<-chan *types.Head, commontypes.Subscription, error)) *RPCClient_SubscribeToFinalizedHeads_Call { - _c.Call.Return(run) - return _c -} - -// SubscribeToHeads provides a mock function with given fields: ctx -func (_m *RPCClient) SubscribeToHeads(ctx context.Context) (<-chan *types.Head, commontypes.Subscription, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for SubscribeToHeads") - } - - var r0 <-chan *types.Head - var r1 commontypes.Subscription - var r2 error - if rf, ok := ret.Get(0).(func(context.Context) (<-chan *types.Head, commontypes.Subscription, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) <-chan *types.Head); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan *types.Head) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) commontypes.Subscription); ok { - r1 = rf(ctx) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(commontypes.Subscription) - } - } - - if rf, ok := ret.Get(2).(func(context.Context) error); ok { - r2 = rf(ctx) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// RPCClient_SubscribeToHeads_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribeToHeads' -type RPCClient_SubscribeToHeads_Call struct { - *mock.Call -} - -// SubscribeToHeads is a helper method to define mock.On call -// - ctx context.Context -func (_e *RPCClient_Expecter) SubscribeToHeads(ctx interface{}) *RPCClient_SubscribeToHeads_Call { - return &RPCClient_SubscribeToHeads_Call{Call: _e.mock.On("SubscribeToHeads", ctx)} -} - -func (_c *RPCClient_SubscribeToHeads_Call) Run(run func(ctx context.Context)) *RPCClient_SubscribeToHeads_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *RPCClient_SubscribeToHeads_Call) Return(ch <-chan *types.Head, sub commontypes.Subscription, err error) *RPCClient_SubscribeToHeads_Call { - _c.Call.Return(ch, sub, err) - return _c -} - -func (_c *RPCClient_SubscribeToHeads_Call) RunAndReturn(run func(context.Context) (<-chan *types.Head, commontypes.Subscription, error)) *RPCClient_SubscribeToHeads_Call { - _c.Call.Return(run) - return _c -} - -// SubscribersCount provides a mock function with given fields: -func (_m *RPCClient) SubscribersCount() int32 { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for SubscribersCount") - } - - var r0 int32 - if rf, ok := ret.Get(0).(func() int32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int32) - } - - return r0 -} - -// RPCClient_SubscribersCount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SubscribersCount' -type RPCClient_SubscribersCount_Call struct { - *mock.Call -} - -// SubscribersCount is a helper method to define mock.On call -func (_e *RPCClient_Expecter) SubscribersCount() *RPCClient_SubscribersCount_Call { - return &RPCClient_SubscribersCount_Call{Call: _e.mock.On("SubscribersCount")} -} - -func (_c *RPCClient_SubscribersCount_Call) Run(run func()) *RPCClient_SubscribersCount_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *RPCClient_SubscribersCount_Call) Return(_a0 int32) *RPCClient_SubscribersCount_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *RPCClient_SubscribersCount_Call) RunAndReturn(run func() int32) *RPCClient_SubscribersCount_Call { - _c.Call.Return(run) - return _c -} - -// SuggestGasPrice provides a mock function with given fields: ctx -func (_m *RPCClient) SuggestGasPrice(ctx context.Context) (*big.Int, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for SuggestGasPrice") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_SuggestGasPrice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SuggestGasPrice' -type RPCClient_SuggestGasPrice_Call struct { - *mock.Call -} - -// SuggestGasPrice is a helper method to define mock.On call -// - ctx context.Context -func (_e *RPCClient_Expecter) SuggestGasPrice(ctx interface{}) *RPCClient_SuggestGasPrice_Call { - return &RPCClient_SuggestGasPrice_Call{Call: _e.mock.On("SuggestGasPrice", ctx)} -} - -func (_c *RPCClient_SuggestGasPrice_Call) Run(run func(ctx context.Context)) *RPCClient_SuggestGasPrice_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *RPCClient_SuggestGasPrice_Call) Return(p *big.Int, err error) *RPCClient_SuggestGasPrice_Call { - _c.Call.Return(p, err) - return _c -} - -func (_c *RPCClient_SuggestGasPrice_Call) RunAndReturn(run func(context.Context) (*big.Int, error)) *RPCClient_SuggestGasPrice_Call { - _c.Call.Return(run) - return _c -} - -// SuggestGasTipCap provides a mock function with given fields: ctx -func (_m *RPCClient) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for SuggestGasTipCap") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_SuggestGasTipCap_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SuggestGasTipCap' -type RPCClient_SuggestGasTipCap_Call struct { - *mock.Call -} - -// SuggestGasTipCap is a helper method to define mock.On call -// - ctx context.Context -func (_e *RPCClient_Expecter) SuggestGasTipCap(ctx interface{}) *RPCClient_SuggestGasTipCap_Call { - return &RPCClient_SuggestGasTipCap_Call{Call: _e.mock.On("SuggestGasTipCap", ctx)} -} - -func (_c *RPCClient_SuggestGasTipCap_Call) Run(run func(ctx context.Context)) *RPCClient_SuggestGasTipCap_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *RPCClient_SuggestGasTipCap_Call) Return(t *big.Int, err error) *RPCClient_SuggestGasTipCap_Call { - _c.Call.Return(t, err) - return _c -} - -func (_c *RPCClient_SuggestGasTipCap_Call) RunAndReturn(run func(context.Context) (*big.Int, error)) *RPCClient_SuggestGasTipCap_Call { - _c.Call.Return(run) - return _c -} - -// TokenBalance provides a mock function with given fields: ctx, accountAddress, tokenAddress -func (_m *RPCClient) TokenBalance(ctx context.Context, accountAddress common.Address, tokenAddress common.Address) (*big.Int, error) { - ret := _m.Called(ctx, accountAddress, tokenAddress) - - if len(ret) == 0 { - panic("no return value specified for TokenBalance") - } - - var r0 *big.Int - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Address) (*big.Int, error)); ok { - return rf(ctx, accountAddress, tokenAddress) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Address) *big.Int); ok { - r0 = rf(ctx, accountAddress, tokenAddress) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*big.Int) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Address, common.Address) error); ok { - r1 = rf(ctx, accountAddress, tokenAddress) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_TokenBalance_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TokenBalance' -type RPCClient_TokenBalance_Call struct { - *mock.Call -} - -// TokenBalance is a helper method to define mock.On call -// - ctx context.Context -// - accountAddress common.Address -// - tokenAddress common.Address -func (_e *RPCClient_Expecter) TokenBalance(ctx interface{}, accountAddress interface{}, tokenAddress interface{}) *RPCClient_TokenBalance_Call { - return &RPCClient_TokenBalance_Call{Call: _e.mock.On("TokenBalance", ctx, accountAddress, tokenAddress)} -} - -func (_c *RPCClient_TokenBalance_Call) Run(run func(ctx context.Context, accountAddress common.Address, tokenAddress common.Address)) *RPCClient_TokenBalance_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Address), args[2].(common.Address)) - }) - return _c -} - -func (_c *RPCClient_TokenBalance_Call) Return(_a0 *big.Int, _a1 error) *RPCClient_TokenBalance_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_TokenBalance_Call) RunAndReturn(run func(context.Context, common.Address, common.Address) (*big.Int, error)) *RPCClient_TokenBalance_Call { - _c.Call.Return(run) - return _c -} - -// TransactionByHash provides a mock function with given fields: ctx, txHash -func (_m *RPCClient) TransactionByHash(ctx context.Context, txHash common.Hash) (*coretypes.Transaction, error) { - ret := _m.Called(ctx, txHash) - - if len(ret) == 0 { - panic("no return value specified for TransactionByHash") - } - - var r0 *coretypes.Transaction - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Transaction, error)); ok { - return rf(ctx, txHash) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Transaction); ok { - r0 = rf(ctx, txHash) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*coretypes.Transaction) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { - r1 = rf(ctx, txHash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_TransactionByHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TransactionByHash' -type RPCClient_TransactionByHash_Call struct { - *mock.Call -} - -// TransactionByHash is a helper method to define mock.On call -// - ctx context.Context -// - txHash common.Hash -func (_e *RPCClient_Expecter) TransactionByHash(ctx interface{}, txHash interface{}) *RPCClient_TransactionByHash_Call { - return &RPCClient_TransactionByHash_Call{Call: _e.mock.On("TransactionByHash", ctx, txHash)} -} - -func (_c *RPCClient_TransactionByHash_Call) Run(run func(ctx context.Context, txHash common.Hash)) *RPCClient_TransactionByHash_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Hash)) - }) - return _c -} - -func (_c *RPCClient_TransactionByHash_Call) Return(_a0 *coretypes.Transaction, _a1 error) *RPCClient_TransactionByHash_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_TransactionByHash_Call) RunAndReturn(run func(context.Context, common.Hash) (*coretypes.Transaction, error)) *RPCClient_TransactionByHash_Call { - _c.Call.Return(run) - return _c -} - -// TransactionReceipt provides a mock function with given fields: ctx, txHash -func (_m *RPCClient) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { - ret := _m.Called(ctx, txHash) - - if len(ret) == 0 { - panic("no return value specified for TransactionReceipt") - } - - var r0 *types.Receipt - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Receipt, error)); ok { - return rf(ctx, txHash) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *types.Receipt); ok { - r0 = rf(ctx, txHash) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.Receipt) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { - r1 = rf(ctx, txHash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_TransactionReceipt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TransactionReceipt' -type RPCClient_TransactionReceipt_Call struct { - *mock.Call -} - -// TransactionReceipt is a helper method to define mock.On call -// - ctx context.Context -// - txHash common.Hash -func (_e *RPCClient_Expecter) TransactionReceipt(ctx interface{}, txHash interface{}) *RPCClient_TransactionReceipt_Call { - return &RPCClient_TransactionReceipt_Call{Call: _e.mock.On("TransactionReceipt", ctx, txHash)} -} - -func (_c *RPCClient_TransactionReceipt_Call) Run(run func(ctx context.Context, txHash common.Hash)) *RPCClient_TransactionReceipt_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Hash)) - }) - return _c -} - -func (_c *RPCClient_TransactionReceipt_Call) Return(_a0 *types.Receipt, _a1 error) *RPCClient_TransactionReceipt_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *RPCClient_TransactionReceipt_Call) RunAndReturn(run func(context.Context, common.Hash) (*types.Receipt, error)) *RPCClient_TransactionReceipt_Call { - _c.Call.Return(run) - return _c -} - -// TransactionReceiptGeth provides a mock function with given fields: ctx, txHash -func (_m *RPCClient) TransactionReceiptGeth(ctx context.Context, txHash common.Hash) (*coretypes.Receipt, error) { - ret := _m.Called(ctx, txHash) - - if len(ret) == 0 { - panic("no return value specified for TransactionReceiptGeth") - } - - var r0 *coretypes.Receipt - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Receipt, error)); ok { - return rf(ctx, txHash) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Receipt); ok { - r0 = rf(ctx, txHash) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*coretypes.Receipt) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { - r1 = rf(ctx, txHash) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RPCClient_TransactionReceiptGeth_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TransactionReceiptGeth' -type RPCClient_TransactionReceiptGeth_Call struct { - *mock.Call -} - -// TransactionReceiptGeth is a helper method to define mock.On call -// - ctx context.Context -// - txHash common.Hash -func (_e *RPCClient_Expecter) TransactionReceiptGeth(ctx interface{}, txHash interface{}) *RPCClient_TransactionReceiptGeth_Call { - return &RPCClient_TransactionReceiptGeth_Call{Call: _e.mock.On("TransactionReceiptGeth", ctx, txHash)} -} - -func (_c *RPCClient_TransactionReceiptGeth_Call) Run(run func(ctx context.Context, txHash common.Hash)) *RPCClient_TransactionReceiptGeth_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Hash)) - }) - return _c -} - -func (_c *RPCClient_TransactionReceiptGeth_Call) Return(r *coretypes.Receipt, err error) *RPCClient_TransactionReceiptGeth_Call { - _c.Call.Return(r, err) - return _c -} - -func (_c *RPCClient_TransactionReceiptGeth_Call) RunAndReturn(run func(context.Context, common.Hash) (*coretypes.Receipt, error)) *RPCClient_TransactionReceiptGeth_Call { - _c.Call.Return(run) - return _c -} - -// UnsubscribeAllExceptAliveLoop provides a mock function with given fields: -func (_m *RPCClient) UnsubscribeAllExceptAliveLoop() { - _m.Called() -} - -// RPCClient_UnsubscribeAllExceptAliveLoop_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnsubscribeAllExceptAliveLoop' -type RPCClient_UnsubscribeAllExceptAliveLoop_Call struct { - *mock.Call -} - -// UnsubscribeAllExceptAliveLoop is a helper method to define mock.On call -func (_e *RPCClient_Expecter) UnsubscribeAllExceptAliveLoop() *RPCClient_UnsubscribeAllExceptAliveLoop_Call { - return &RPCClient_UnsubscribeAllExceptAliveLoop_Call{Call: _e.mock.On("UnsubscribeAllExceptAliveLoop")} -} - -func (_c *RPCClient_UnsubscribeAllExceptAliveLoop_Call) Run(run func()) *RPCClient_UnsubscribeAllExceptAliveLoop_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *RPCClient_UnsubscribeAllExceptAliveLoop_Call) Return() *RPCClient_UnsubscribeAllExceptAliveLoop_Call { - _c.Call.Return() - return _c -} - -func (_c *RPCClient_UnsubscribeAllExceptAliveLoop_Call) RunAndReturn(run func()) *RPCClient_UnsubscribeAllExceptAliveLoop_Call { - _c.Call.Return(run) - return _c -} - -// NewRPCClient creates a new instance of RPCClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewRPCClient(t interface { - mock.TestingT - Cleanup(func()) -}) *RPCClient { - mock := &RPCClient{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/chains/evm/client/null_client.go b/core/chains/evm/client/null_client.go index 5b1a4d7e1bb..c59861a7379 100644 --- a/core/chains/evm/client/null_client.go +++ b/core/chains/evm/client/null_client.go @@ -90,9 +90,9 @@ func (nc *NullClient) SubscribeFilterLogs(ctx context.Context, q ethereum.Filter return newNullSubscription(nc.lggr), nil } -func (nc *NullClient) SubscribeNewHead(ctx context.Context, ch chan<- *evmtypes.Head) (ethereum.Subscription, error) { - nc.lggr.Debug("SubscribeNewHead") - return newNullSubscription(nc.lggr), nil +func (nc *NullClient) SubscribeToHeads(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { + nc.lggr.Debug("SubscribeToHeads") + return nil, newNullSubscription(nc.lggr), nil } // diff --git a/core/chains/evm/client/null_client_test.go b/core/chains/evm/client/null_client_test.go index bc6c166030f..6d61a3e808f 100644 --- a/core/chains/evm/client/null_client_test.go +++ b/core/chains/evm/client/null_client_test.go @@ -15,7 +15,6 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ) func TestNullClient(t *testing.T) { @@ -62,10 +61,9 @@ func TestNullClient(t *testing.T) { require.Nil(t, h) require.Equal(t, 1, logs.FilterMessage("HeadByNumber").Len()) - chHeads := make(chan *evmtypes.Head) - sub, err := nc.SubscribeNewHead(ctx, chHeads) + _, sub, err := nc.SubscribeToHeads(ctx) require.NoError(t, err) - require.Equal(t, 1, logs.FilterMessage("SubscribeNewHead").Len()) + require.Equal(t, 1, logs.FilterMessage("SubscribeToHeads").Len()) require.Nil(t, sub.Err()) require.Equal(t, 1, logs.FilterMessage("Err").Len()) sub.Unsubscribe() diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index f55c35980df..79d6bc9214f 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -29,6 +29,7 @@ import ( commonclient "github.com/smartcontractkit/chainlink/v2/common/client" commontypes "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" @@ -77,35 +78,6 @@ var ( }, []string{"evmChainID", "nodeName", "rpcHost", "isSendOnly", "success", "rpcCallName"}) ) -// RPCClient includes all the necessary generalized RPC methods along with any additional chain-specific methods. -type RPCClient interface { - commonclient.RPC[ - *big.Int, - evmtypes.Nonce, - common.Address, - common.Hash, - *types.Transaction, - common.Hash, - types.Log, - ethereum.FilterQuery, - *evmtypes.Receipt, - *assets.Wei, - *evmtypes.Head, - rpc.BatchElem, - ] - BlockByHashGeth(ctx context.Context, hash common.Hash) (b *types.Block, err error) - BlockByNumberGeth(ctx context.Context, number *big.Int) (b *types.Block, err error) - HeaderByHash(ctx context.Context, h common.Hash) (head *types.Header, err error) - HeaderByNumber(ctx context.Context, n *big.Int) (head *types.Header, err error) - PendingCodeAt(ctx context.Context, account common.Address) (b []byte, err error) - SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (s ethereum.Subscription, err error) - SuggestGasPrice(ctx context.Context) (p *big.Int, err error) - SuggestGasTipCap(ctx context.Context) (t *big.Int, err error) - TransactionReceiptGeth(ctx context.Context, txHash common.Hash) (r *types.Receipt, err error) - GetInterceptedChainInfo() (latest, highestUserObservations commonclient.ChainInfo) - FeeHistory(ctx context.Context, blockCount uint64, rewardPercentiles []float64) (feeHistory *ethereum.FeeHistory, err error) -} - const rpcSubscriptionMethodNewHeads = "newHeads" type rawclient struct { @@ -114,16 +86,17 @@ type rawclient struct { uri url.URL } -type rpcClient struct { +type RPCClient struct { + cfg config.NodePool rpcLog logger.SugaredLogger name string id int chainID *big.Int tier commonclient.NodeTier - largePayloadRpcTimeout time.Duration - rpcTimeout time.Duration + largePayloadRPCTimeout time.Duration finalizedBlockPollInterval time.Duration newHeadsPollInterval time.Duration + rpcTimeout time.Duration chainType chaintype.ChainType ws *rawclient @@ -134,25 +107,25 @@ type rpcClient struct { // Need to track subscriptions because closing the RPC does not (always?) // close the underlying subscription - subs []ethereum.Subscription - - // Need to track the aliveLoop subscription, so we do not cancel it when checking lease on the MultiNode - aliveLoopSub ethereum.Subscription + subs map[ethereum.Subscription]struct{} // chStopInFlight can be closed to immediately cancel all in-flight requests on - // this rpcClient. Closing and replacing should be serialized through - // stateMu since it can happen on state transitions as well as rpcClient Close. + // this RPCClient. Closing and replacing should be serialized through + // stateMu since it can happen on state transitions as well as RPCClient Close. chStopInFlight chan struct{} chainInfoLock sync.RWMutex - // intercepted values seen by callers of the rpcClient excluding health check calls. Need to ensure MultiNode provides repeatable read guarantee + // intercepted values seen by callers of the RPCClient excluding health check calls. Need to ensure MultiNode provides repeatable read guarantee highestUserObservations commonclient.ChainInfo // most recent chain info observed during current lifecycle (reseted on DisconnectAll) latestChainInfo commonclient.ChainInfo } -// NewRPCCLient returns a new *rpcClient as commonclient.RPC +var _ commonclient.RPCClient[*big.Int, *evmtypes.Head] = (*RPCClient)(nil) +var _ commonclient.SendTxRPCClient[*types.Transaction] = (*RPCClient)(nil) + func NewRPCClient( + cfg config.NodePool, lggr logger.Logger, wsuri *url.URL, httpuri *url.URL, @@ -160,23 +133,22 @@ func NewRPCClient( id int, chainID *big.Int, tier commonclient.NodeTier, - finalizedBlockPollInterval time.Duration, - newHeadsPollInterval time.Duration, - largePayloadRpcTimeout time.Duration, + largePayloadRPCTimeout time.Duration, rpcTimeout time.Duration, chainType chaintype.ChainType, -) RPCClient { - r := &rpcClient{ - largePayloadRpcTimeout: largePayloadRpcTimeout, +) *RPCClient { + r := &RPCClient{ + largePayloadRPCTimeout: largePayloadRPCTimeout, rpcTimeout: rpcTimeout, chainType: chainType, } + r.cfg = cfg r.name = name r.id = id r.chainID = chainID r.tier = tier - r.finalizedBlockPollInterval = finalizedBlockPollInterval - r.newHeadsPollInterval = newHeadsPollInterval + r.finalizedBlockPollInterval = cfg.FinalizedBlockPollInterval() + r.newHeadsPollInterval = cfg.NewHeadsPollInterval() if wsuri != nil { r.ws = &rawclient{uri: *wsuri} } @@ -192,12 +164,39 @@ func NewRPCClient( "evmChainID", chainID, ) r.rpcLog = logger.Sugared(lggr).Named("RPC") + r.subs = map[ethereum.Subscription]struct{}{} return r } +func (r *RPCClient) Ping(ctx context.Context) error { + version, err := r.ClientVersion(ctx) + if err != nil { + return fmt.Errorf("ping failed: %w", err) + } + r.rpcLog.Debugf("ping client version: %s", version) + return err +} + +func (r *RPCClient) UnsubscribeAllExcept(subs ...commontypes.Subscription) { + r.subsSliceMu.Lock() + defer r.subsSliceMu.Unlock() + + keepSubs := map[commontypes.Subscription]struct{}{} + for _, sub := range subs { + keepSubs[sub] = struct{}{} + } + + for sub := range r.subs { + if _, keep := keepSubs[sub]; !keep { + sub.Unsubscribe() + delete(r.subs, sub) + } + } +} + // Not thread-safe, pure dial. -func (r *rpcClient) Dial(callerCtx context.Context) error { +func (r *RPCClient) Dial(callerCtx context.Context) error { ctx, cancel := r.makeQueryCtx(callerCtx, r.rpcTimeout) defer cancel() @@ -234,7 +233,7 @@ func (r *rpcClient) Dial(callerCtx context.Context) error { // Not thread-safe, pure dial. // DialHTTP doesn't actually make any external HTTP calls // It can only return error if the URL is malformed. -func (r *rpcClient) DialHTTP() error { +func (r *RPCClient) DialHTTP() error { promEVMPoolRPCNodeDials.WithLabelValues(r.chainID.String(), r.name).Inc() lggr := r.rpcLog.With("httpuri", r.http.uri.Redacted()) lggr.Debugw("RPC dial: evmclient.Client#dial") @@ -254,27 +253,28 @@ func (r *rpcClient) DialHTTP() error { return nil } -func (r *rpcClient) Close() { +func (r *RPCClient) Close() { defer func() { if r.ws != nil && r.ws.rpc != nil { r.ws.rpc.Close() } }() - - r.stateMu.Lock() - defer r.stateMu.Unlock() r.cancelInflightRequests() + r.UnsubscribeAllExcept() + r.chainInfoLock.Lock() + r.latestChainInfo = commonclient.ChainInfo{} + r.chainInfoLock.Unlock() } // cancelInflightRequests closes and replaces the chStopInFlight -// WARNING: NOT THREAD-SAFE -// This must be called from within the r.stateMu lock -func (r *rpcClient) cancelInflightRequests() { +func (r *RPCClient) cancelInflightRequests() { + r.stateMu.Lock() + defer r.stateMu.Unlock() close(r.chStopInFlight) r.chStopInFlight = make(chan struct{}) } -func (r *rpcClient) String() string { +func (r *RPCClient) String() string { s := fmt.Sprintf("(%s)%s", r.tier.String(), r.name) if r.ws != nil { s = s + fmt.Sprintf(":%s", r.ws.uri.Redacted()) @@ -285,7 +285,7 @@ func (r *rpcClient) String() string { return s } -func (r *rpcClient) logResult( +func (r *RPCClient) logResult( lggr logger.Logger, err error, callDuration time.Duration, @@ -308,7 +308,7 @@ func (r *rpcClient) logResult( promEVMPoolRPCCallTiming. WithLabelValues( r.chainID.String(), // chain id - r.name, // rpcClient name + r.name, // RPCClient name rpcDomain, // rpc domain "false", // is send only strconv.FormatBool(err == nil), // is successful @@ -317,18 +317,18 @@ func (r *rpcClient) logResult( Observe(float64(callDuration)) } -func (r *rpcClient) getRPCDomain() string { +func (r *RPCClient) getRPCDomain() string { if r.http != nil { return r.http.uri.Host } return r.ws.uri.Host } -// registerSub adds the sub to the rpcClient list -func (r *rpcClient) registerSub(sub ethereum.Subscription, stopInFLightCh chan struct{}) error { +// registerSub adds the sub to the RPCClient list +func (r *RPCClient) registerSub(sub ethereum.Subscription, stopInFLightCh chan struct{}) error { r.subsSliceMu.Lock() defer r.subsSliceMu.Unlock() - // ensure that the `sub` belongs to current life cycle of the `rpcClient` and it should not be killed due to + // ensure that the `sub` belongs to current life cycle of the `RPCClient` and it should not be killed due to // previous `DisconnectAll` call. select { case <-stopInFLightCh: @@ -337,69 +337,15 @@ func (r *rpcClient) registerSub(sub ethereum.Subscription, stopInFLightCh chan s default: } // TODO: BCI-3358 - delete sub when caller unsubscribes. - r.subs = append(r.subs, sub) + r.subs[sub] = struct{}{} return nil } -// DisconnectAll disconnects all clients connected to the rpcClient -func (r *rpcClient) DisconnectAll() { - r.stateMu.Lock() - if r.ws != nil && r.ws.rpc != nil { - r.ws.rpc.Close() - } - r.cancelInflightRequests() - r.stateMu.Unlock() - - r.subsSliceMu.Lock() - r.unsubscribeAll() - r.subsSliceMu.Unlock() - - r.chainInfoLock.Lock() - r.latestChainInfo = commonclient.ChainInfo{} - r.chainInfoLock.Unlock() -} - -// unsubscribeAll unsubscribes all subscriptions -// WARNING: NOT THREAD-SAFE -// This must be called from within the r.stateMu lock -func (r *rpcClient) unsubscribeAll() { - for _, sub := range r.subs { - sub.Unsubscribe() - } - r.subs = nil -} -func (r *rpcClient) SetAliveLoopSub(sub commontypes.Subscription) { - r.stateMu.Lock() - defer r.stateMu.Unlock() - - r.aliveLoopSub = sub -} - -// SubscribersCount returns the number of client subscribed to the node -func (r *rpcClient) SubscribersCount() int32 { - r.stateMu.RLock() - defer r.stateMu.RUnlock() - return int32(len(r.subs)) -} - -// UnsubscribeAllExceptAliveLoop disconnects all subscriptions to the node except the alive loop subscription -// while holding the n.stateMu lock -func (r *rpcClient) UnsubscribeAllExceptAliveLoop() { - r.stateMu.Lock() - defer r.stateMu.Unlock() - - for _, s := range r.subs { - if s != r.aliveLoopSub { - s.Unsubscribe() - } - } -} - // RPC wrappers // CallContext implementation -func (r *rpcClient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { - ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.largePayloadRpcTimeout) +func (r *RPCClient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { + ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.largePayloadRPCTimeout) defer cancel() lggr := r.newRqLggr().With( "method", method, @@ -421,7 +367,7 @@ func (r *rpcClient) CallContext(ctx context.Context, result interface{}, method return err } -func (r *rpcClient) BatchCallContext(rootCtx context.Context, b []rpc.BatchElem) error { +func (r *RPCClient) BatchCallContext(rootCtx context.Context, b []rpc.BatchElem) error { // Astar's finality tags provide weaker finality guarantees than we require. // Fetch latest finalized block using Astar's custom requests and populate it after batch request completes var astarRawLatestFinalizedBlock json.RawMessage @@ -442,7 +388,7 @@ func (r *rpcClient) BatchCallContext(rootCtx context.Context, b []rpc.BatchElem) } } - ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(rootCtx, r.largePayloadRpcTimeout) + ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(rootCtx, r.largePayloadRPCTimeout) defer cancel() lggr := r.newRqLggr().With("nBatchElems", len(b), "batchElems", b) @@ -499,73 +445,7 @@ func isRequestingFinalizedBlock(el rpc.BatchElem) bool { } } -// TODO: Full transition from SubscribeNewHead to SubscribeToHeads is done in BCI-2875 -func (r *rpcClient) SubscribeNewHead(ctx context.Context, channel chan<- *evmtypes.Head) (_ commontypes.Subscription, err error) { - ctx, cancel, chStopInFlight, ws, _ := r.acquireQueryCtx(ctx, r.rpcTimeout) - defer cancel() - args := []interface{}{"newHeads"} - lggr := r.newRqLggr().With("args", args) - if r.newHeadsPollInterval > 0 { - interval := r.newHeadsPollInterval - timeout := interval - poller, pollerCh := commonclient.NewPoller[*evmtypes.Head](interval, r.latestBlock, timeout, r.rpcLog) - if err = poller.Start(ctx); err != nil { - return nil, err - } - - // NOTE this is a temporary special treatment for SubscribeNewHead before we refactor head tracker to use SubscribeToHeads - // as we need to forward new head from the poller channel to the channel passed from caller. - go func() { - for head := range pollerCh { - select { - case channel <- head: - // forwarding new head to the channel passed from caller - case <-poller.Err(): - // return as poller returns error - return - } - } - }() - - err = r.registerSub(&poller, chStopInFlight) - if err != nil { - return nil, err - } - - lggr.Debugf("Polling new heads over http") - return &poller, nil - } - - if ws == nil { - return nil, errors.New("SubscribeNewHead is not allowed without ws url") - } - - lggr.Debug("RPC call: evmclient.Client#EthSubscribe") - start := time.Now() - defer func() { - duration := time.Since(start) - r.logResult(lggr, err, duration, r.getRPCDomain(), "EthSubscribe") - err = r.wrapWS(err) - }() - subForwarder := newSubForwarder(channel, func(head *evmtypes.Head) *evmtypes.Head { - head.EVMChainID = ubig.New(r.chainID) - r.onNewHead(ctx, chStopInFlight, head) - return head - }, r.wrapRPCClientError) - err = subForwarder.start(ws.rpc.EthSubscribe(ctx, subForwarder.srcCh, args...)) - if err != nil { - return - } - - err = r.registerSub(subForwarder, chStopInFlight) - if err != nil { - return - } - - return subForwarder, nil -} - -func (r *rpcClient) SubscribeToHeads(ctx context.Context) (ch <-chan *evmtypes.Head, sub commontypes.Subscription, err error) { +func (r *RPCClient) SubscribeToHeads(ctx context.Context) (ch <-chan *evmtypes.Head, sub commontypes.Subscription, err error) { ctx, cancel, chStopInFlight, ws, _ := r.acquireQueryCtx(ctx, r.rpcTimeout) defer cancel() args := []interface{}{rpcSubscriptionMethodNewHeads} @@ -621,10 +501,11 @@ func (r *rpcClient) SubscribeToHeads(ctx context.Context) (ch <-chan *evmtypes.H return channel, forwarder, err } -func (r *rpcClient) SubscribeToFinalizedHeads(ctx context.Context) (<-chan *evmtypes.Head, commontypes.Subscription, error) { +func (r *RPCClient) SubscribeToFinalizedHeads(ctx context.Context) (<-chan *evmtypes.Head, commontypes.Subscription, error) { ctx, cancel, chStopInFlight, _, _ := r.acquireQueryCtx(ctx, r.rpcTimeout) defer cancel() - interval := r.finalizedBlockPollInterval + + interval := r.cfg.FinalizedBlockPollInterval() if interval == 0 { return nil, nil, errors.New("FinalizedBlockPollInterval is 0") } @@ -644,7 +525,7 @@ func (r *rpcClient) SubscribeToFinalizedHeads(ctx context.Context) (<-chan *evmt // GethClient wrappers -func (r *rpcClient) TransactionReceipt(ctx context.Context, txHash common.Hash) (receipt *evmtypes.Receipt, err error) { +func (r *RPCClient) TransactionReceipt(ctx context.Context, txHash common.Hash) (receipt *evmtypes.Receipt, err error) { err = r.CallContext(ctx, &receipt, "eth_getTransactionReceipt", txHash, false) if err != nil { return nil, err @@ -656,7 +537,7 @@ func (r *rpcClient) TransactionReceipt(ctx context.Context, txHash common.Hash) return } -func (r *rpcClient) TransactionReceiptGeth(ctx context.Context, txHash common.Hash) (receipt *types.Receipt, err error) { +func (r *RPCClient) TransactionReceiptGeth(ctx context.Context, txHash common.Hash) (receipt *types.Receipt, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("txHash", txHash) @@ -679,8 +560,7 @@ func (r *rpcClient) TransactionReceiptGeth(ctx context.Context, txHash common.Ha return } - -func (r *rpcClient) TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, err error) { +func (r *RPCClient) TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("txHash", txHash) @@ -704,7 +584,7 @@ func (r *rpcClient) TransactionByHash(ctx context.Context, txHash common.Hash) ( return } -func (r *rpcClient) HeaderByNumber(ctx context.Context, number *big.Int) (header *types.Header, err error) { +func (r *RPCClient) HeaderByNumber(ctx context.Context, number *big.Int) (header *types.Header, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("number", number) @@ -725,7 +605,7 @@ func (r *rpcClient) HeaderByNumber(ctx context.Context, number *big.Int) (header return } -func (r *rpcClient) HeaderByHash(ctx context.Context, hash common.Hash) (header *types.Header, err error) { +func (r *RPCClient) HeaderByHash(ctx context.Context, hash common.Hash) (header *types.Header, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("hash", hash) @@ -748,7 +628,7 @@ func (r *rpcClient) HeaderByHash(ctx context.Context, hash common.Hash) (header return } -func (r *rpcClient) LatestFinalizedBlock(ctx context.Context) (head *evmtypes.Head, err error) { +func (r *RPCClient) LatestFinalizedBlock(ctx context.Context) (head *evmtypes.Head, err error) { // capture chStopInFlight to ensure we are not updating chainInfo with observations related to previous life cycle ctx, cancel, chStopInFlight, _, _ := r.acquireQueryCtx(ctx, r.rpcTimeout) defer cancel() @@ -774,11 +654,11 @@ func (r *rpcClient) LatestFinalizedBlock(ctx context.Context) (head *evmtypes.He return } -func (r *rpcClient) latestBlock(ctx context.Context) (head *evmtypes.Head, err error) { +func (r *RPCClient) latestBlock(ctx context.Context) (head *evmtypes.Head, err error) { return r.BlockByNumber(ctx, nil) } -func (r *rpcClient) astarLatestFinalizedBlock(ctx context.Context, result interface{}) (err error) { +func (r *RPCClient) astarLatestFinalizedBlock(ctx context.Context, result interface{}) (err error) { var hashResult string err = r.CallContext(ctx, &hashResult, "chain_getFinalizedHead") if err != nil { @@ -805,7 +685,7 @@ func (r *rpcClient) astarLatestFinalizedBlock(ctx context.Context, result interf return nil } -func (r *rpcClient) BlockByNumber(ctx context.Context, number *big.Int) (head *evmtypes.Head, err error) { +func (r *RPCClient) BlockByNumber(ctx context.Context, number *big.Int) (head *evmtypes.Head, err error) { ctx, cancel, chStopInFlight, _, _ := r.acquireQueryCtx(ctx, r.rpcTimeout) defer cancel() hexNumber := ToBlockNumArg(number) @@ -828,7 +708,7 @@ func (r *rpcClient) BlockByNumber(ctx context.Context, number *big.Int) (head *e return } -func (r *rpcClient) ethGetBlockByNumber(ctx context.Context, number string, result interface{}) (err error) { +func (r *RPCClient) ethGetBlockByNumber(ctx context.Context, number string, result interface{}) (err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() const method = "eth_getBlockByNumber" @@ -851,7 +731,7 @@ func (r *rpcClient) ethGetBlockByNumber(ctx context.Context, number string, resu return err } -func (r *rpcClient) BlockByHash(ctx context.Context, hash common.Hash) (head *evmtypes.Head, err error) { +func (r *RPCClient) BlockByHash(ctx context.Context, hash common.Hash) (head *evmtypes.Head, err error) { err = r.CallContext(ctx, &head, "eth_getBlockByHash", hash.Hex(), false) if err != nil { return nil, err @@ -864,7 +744,7 @@ func (r *rpcClient) BlockByHash(ctx context.Context, hash common.Hash) (head *ev return } -func (r *rpcClient) BlockByHashGeth(ctx context.Context, hash common.Hash) (block *types.Block, err error) { +func (r *RPCClient) BlockByHashGeth(ctx context.Context, hash common.Hash) (block *types.Block, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("hash", hash) @@ -887,7 +767,7 @@ func (r *rpcClient) BlockByHashGeth(ctx context.Context, hash common.Hash) (bloc return } -func (r *rpcClient) BlockByNumberGeth(ctx context.Context, number *big.Int) (block *types.Block, err error) { +func (r *RPCClient) BlockByNumberGeth(ctx context.Context, number *big.Int) (block *types.Block, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("number", number) @@ -910,8 +790,8 @@ func (r *rpcClient) BlockByNumberGeth(ctx context.Context, number *big.Int) (blo return } -func (r *rpcClient) SendTransaction(ctx context.Context, tx *types.Transaction) error { - ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.largePayloadRpcTimeout) +func (r *RPCClient) SendTransaction(ctx context.Context, tx *types.Transaction) error { + ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.largePayloadRPCTimeout) defer cancel() lggr := r.newRqLggr().With("tx", tx) @@ -930,12 +810,12 @@ func (r *rpcClient) SendTransaction(ctx context.Context, tx *types.Transaction) return err } -func (r *rpcClient) SimulateTransaction(ctx context.Context, tx *types.Transaction) error { +func (r *RPCClient) SimulateTransaction(ctx context.Context, tx *types.Transaction) error { // Not Implemented return pkgerrors.New("SimulateTransaction not implemented") } -func (r *rpcClient) SendEmptyTransaction( +func (r *RPCClient) SendEmptyTransaction( ctx context.Context, newTxAttempt func(nonce evmtypes.Nonce, feeLimit uint32, fee *assets.Wei, fromAddress common.Address) (attempt any, err error), nonce evmtypes.Nonce, @@ -948,7 +828,7 @@ func (r *rpcClient) SendEmptyTransaction( } // PendingSequenceAt returns one higher than the highest nonce from both mempool and mined transactions -func (r *rpcClient) PendingSequenceAt(ctx context.Context, account common.Address) (nonce evmtypes.Nonce, err error) { +func (r *RPCClient) PendingSequenceAt(ctx context.Context, account common.Address) (nonce evmtypes.Nonce, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("account", account) @@ -977,7 +857,7 @@ func (r *rpcClient) PendingSequenceAt(ctx context.Context, account common.Addres // SequenceAt is a bit of a misnomer. You might expect it to return the highest // mined nonce at the given block number, but it actually returns the total // transaction count which is the highest mined nonce + 1 -func (r *rpcClient) SequenceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (nonce evmtypes.Nonce, err error) { +func (r *RPCClient) SequenceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (nonce evmtypes.Nonce, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("account", account, "blockNumber", blockNumber) @@ -1003,7 +883,7 @@ func (r *rpcClient) SequenceAt(ctx context.Context, account common.Address, bloc return } -func (r *rpcClient) PendingCodeAt(ctx context.Context, account common.Address) (code []byte, err error) { +func (r *RPCClient) PendingCodeAt(ctx context.Context, account common.Address) (code []byte, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("account", account) @@ -1026,7 +906,7 @@ func (r *rpcClient) PendingCodeAt(ctx context.Context, account common.Address) ( return } -func (r *rpcClient) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) (code []byte, err error) { +func (r *RPCClient) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) (code []byte, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("account", account, "blockNumber", blockNumber) @@ -1049,8 +929,8 @@ func (r *rpcClient) CodeAt(ctx context.Context, account common.Address, blockNum return } -func (r *rpcClient) EstimateGas(ctx context.Context, c interface{}) (gas uint64, err error) { - ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.largePayloadRpcTimeout) +func (r *RPCClient) EstimateGas(ctx context.Context, c interface{}) (gas uint64, err error) { + ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.largePayloadRPCTimeout) defer cancel() call := c.(ethereum.CallMsg) lggr := r.newRqLggr().With("call", call) @@ -1073,7 +953,7 @@ func (r *rpcClient) EstimateGas(ctx context.Context, c interface{}) (gas uint64, return } -func (r *rpcClient) SuggestGasPrice(ctx context.Context) (price *big.Int, err error) { +func (r *RPCClient) SuggestGasPrice(ctx context.Context) (price *big.Int, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr() @@ -1096,8 +976,8 @@ func (r *rpcClient) SuggestGasPrice(ctx context.Context) (price *big.Int, err er return } -func (r *rpcClient) CallContract(ctx context.Context, msg interface{}, blockNumber *big.Int) (val []byte, err error) { - ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.largePayloadRpcTimeout) +func (r *RPCClient) CallContract(ctx context.Context, msg interface{}, blockNumber *big.Int) (val []byte, err error) { + ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.largePayloadRPCTimeout) defer cancel() lggr := r.newRqLggr().With("callMsg", msg, "blockNumber", blockNumber) message := msg.(ethereum.CallMsg) @@ -1124,8 +1004,8 @@ func (r *rpcClient) CallContract(ctx context.Context, msg interface{}, blockNumb return } -func (r *rpcClient) PendingCallContract(ctx context.Context, msg interface{}) (val []byte, err error) { - ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.largePayloadRpcTimeout) +func (r *RPCClient) PendingCallContract(ctx context.Context, msg interface{}) (val []byte, err error) { + ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.largePayloadRPCTimeout) defer cancel() lggr := r.newRqLggr().With("callMsg", msg) message := msg.(ethereum.CallMsg) @@ -1152,13 +1032,13 @@ func (r *rpcClient) PendingCallContract(ctx context.Context, msg interface{}) (v return } -func (r *rpcClient) LatestBlockHeight(ctx context.Context) (*big.Int, error) { +func (r *RPCClient) LatestBlockHeight(ctx context.Context) (*big.Int, error) { var height big.Int h, err := r.BlockNumber(ctx) return height.SetUint64(h), err } -func (r *rpcClient) BlockNumber(ctx context.Context) (height uint64, err error) { +func (r *RPCClient) BlockNumber(ctx context.Context) (height uint64, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr() @@ -1181,7 +1061,7 @@ func (r *rpcClient) BlockNumber(ctx context.Context) (height uint64, err error) return } -func (r *rpcClient) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (balance *big.Int, err error) { +func (r *RPCClient) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (balance *big.Int, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("account", account.Hex(), "blockNumber", blockNumber) @@ -1204,7 +1084,7 @@ func (r *rpcClient) BalanceAt(ctx context.Context, account common.Address, block return } -func (r *rpcClient) FeeHistory(ctx context.Context, blockCount uint64, rewardPercentiles []float64) (feeHistory *ethereum.FeeHistory, err error) { +func (r *RPCClient) FeeHistory(ctx context.Context, blockCount uint64, rewardPercentiles []float64) (feeHistory *ethereum.FeeHistory, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("blockCount", blockCount, "rewardPercentiles", rewardPercentiles) @@ -1237,7 +1117,7 @@ type CallArgs struct { } // TokenBalance returns the balance of the given address for the token contract address. -func (r *rpcClient) TokenBalance(ctx context.Context, address common.Address, contractAddress common.Address) (*big.Int, error) { +func (r *RPCClient) TokenBalance(ctx context.Context, address common.Address, contractAddress common.Address) (*big.Int, error) { result := "" numLinkBigInt := new(big.Int) functionSelector := evmtypes.HexToFunctionSelector(BALANCE_OF_ADDRESS_FUNCTION_SELECTOR) // balanceOf(address) @@ -1257,7 +1137,7 @@ func (r *rpcClient) TokenBalance(ctx context.Context, address common.Address, co } // LINKBalance returns the balance of LINK at the given address -func (r *rpcClient) LINKBalance(ctx context.Context, address common.Address, linkAddress common.Address) (*commonassets.Link, error) { +func (r *RPCClient) LINKBalance(ctx context.Context, address common.Address, linkAddress common.Address) (*commonassets.Link, error) { balance, err := r.TokenBalance(ctx, address, linkAddress) if err != nil { return commonassets.NewLinkFromJuels(0), err @@ -1265,11 +1145,11 @@ func (r *rpcClient) LINKBalance(ctx context.Context, address common.Address, lin return (*commonassets.Link)(balance), nil } -func (r *rpcClient) FilterEvents(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { +func (r *RPCClient) FilterEvents(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { return r.FilterLogs(ctx, q) } -func (r *rpcClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) (l []types.Log, err error) { +func (r *RPCClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) (l []types.Log, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr().With("q", q) @@ -1292,12 +1172,12 @@ func (r *rpcClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) (l [ return } -func (r *rpcClient) ClientVersion(ctx context.Context) (version string, err error) { +func (r *RPCClient) ClientVersion(ctx context.Context) (version string, err error) { err = r.CallContext(ctx, &version, "web3_clientVersion") return } -func (r *rpcClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (_ ethereum.Subscription, err error) { +func (r *RPCClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (_ ethereum.Subscription, err error) { ctx, cancel, chStopInFlight, ws, _ := r.acquireQueryCtx(ctx, r.rpcTimeout) defer cancel() if ws == nil { @@ -1326,7 +1206,7 @@ func (r *rpcClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQu return sub, nil } -func (r *rpcClient) SuggestGasTipCap(ctx context.Context) (tipCap *big.Int, err error) { +func (r *RPCClient) SuggestGasTipCap(ctx context.Context) (tipCap *big.Int, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr() @@ -1351,7 +1231,7 @@ func (r *rpcClient) SuggestGasTipCap(ctx context.Context) (tipCap *big.Int, err // Returns the ChainID according to the geth client. This is useful for functions like verify() // the common node. -func (r *rpcClient) ChainID(ctx context.Context) (chainID *big.Int, err error) { +func (r *RPCClient) ChainID(ctx context.Context) (chainID *big.Int, err error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() @@ -1367,16 +1247,16 @@ func (r *rpcClient) ChainID(ctx context.Context) (chainID *big.Int, err error) { } // newRqLggr generates a new logger with a unique request ID -func (r *rpcClient) newRqLggr() logger.SugaredLogger { +func (r *RPCClient) newRqLggr() logger.SugaredLogger { return r.rpcLog.With("requestID", uuid.New()) } -func (r *rpcClient) wrapRPCClientError(err error) error { +func (r *RPCClient) wrapRPCClientError(err error) error { // simple add msg to the error without adding new stack trace return pkgerrors.WithMessage(err, r.rpcClientErrorPrefix()) } -func (r *rpcClient) rpcClientErrorPrefix() string { +func (r *RPCClient) rpcClientErrorPrefix() string { return fmt.Sprintf("RPCClient returned error (%s)", r.name) } @@ -1390,12 +1270,12 @@ func wrapCallError(err error, tp string) error { return pkgerrors.Wrapf(err, "%s call failed", tp) } -func (r *rpcClient) wrapWS(err error) error { +func (r *RPCClient) wrapWS(err error) error { err = wrapCallError(err, fmt.Sprintf("%s websocket (%s)", r.tier.String(), r.ws.uri.Redacted())) return r.wrapRPCClientError(err) } -func (r *rpcClient) wrapHTTP(err error) error { +func (r *RPCClient) wrapHTTP(err error) error { err = wrapCallError(err, fmt.Sprintf("%s http (%s)", r.tier.String(), r.http.uri.Redacted())) err = r.wrapRPCClientError(err) if err != nil { @@ -1407,12 +1287,12 @@ func (r *rpcClient) wrapHTTP(err error) error { } // makeLiveQueryCtxAndSafeGetClients wraps makeQueryCtx -func (r *rpcClient) makeLiveQueryCtxAndSafeGetClients(parentCtx context.Context, timeout time.Duration) (ctx context.Context, cancel context.CancelFunc, ws *rawclient, http *rawclient) { +func (r *RPCClient) makeLiveQueryCtxAndSafeGetClients(parentCtx context.Context, timeout time.Duration) (ctx context.Context, cancel context.CancelFunc, ws *rawclient, http *rawclient) { ctx, cancel, _, ws, http = r.acquireQueryCtx(parentCtx, timeout) return } -func (r *rpcClient) acquireQueryCtx(parentCtx context.Context, timeout time.Duration) (ctx context.Context, cancel context.CancelFunc, +func (r *RPCClient) acquireQueryCtx(parentCtx context.Context, timeout time.Duration) (ctx context.Context, cancel context.CancelFunc, chStopInFlight chan struct{}, ws *rawclient, http *rawclient) { // Need to wrap in mutex because state transition can cancel and replace the // context @@ -1446,11 +1326,11 @@ func makeQueryCtx(ctx context.Context, ch services.StopChan, timeout time.Durati return ctx, cancel } -func (r *rpcClient) makeQueryCtx(ctx context.Context, timeout time.Duration) (context.Context, context.CancelFunc) { +func (r *RPCClient) makeQueryCtx(ctx context.Context, timeout time.Duration) (context.Context, context.CancelFunc) { return makeQueryCtx(ctx, r.getChStopInflight(), timeout) } -func (r *rpcClient) IsSyncing(ctx context.Context) (bool, error) { +func (r *RPCClient) IsSyncing(ctx context.Context) (bool, error) { ctx, cancel, ws, http := r.makeLiveQueryCtxAndSafeGetClients(ctx, r.rpcTimeout) defer cancel() lggr := r.newRqLggr() @@ -1477,21 +1357,17 @@ func (r *rpcClient) IsSyncing(ctx context.Context) (bool, error) { // getChStopInflight provides a convenience helper that mutex wraps a // read to the chStopInFlight -func (r *rpcClient) getChStopInflight() chan struct{} { +func (r *RPCClient) getChStopInflight() chan struct{} { r.stateMu.RLock() defer r.stateMu.RUnlock() return r.chStopInFlight } -func (r *rpcClient) Name() string { - return r.name -} - -func Name(r *rpcClient) string { +func (r *RPCClient) Name() string { return r.name } -func (r *rpcClient) onNewHead(ctx context.Context, requestCh <-chan struct{}, head *evmtypes.Head) { +func (r *RPCClient) onNewHead(ctx context.Context, requestCh <-chan struct{}, head *evmtypes.Head) { if head == nil { return } @@ -1503,7 +1379,7 @@ func (r *rpcClient) onNewHead(ctx context.Context, requestCh <-chan struct{}, he r.highestUserObservations.TotalDifficulty = commonclient.MaxTotalDifficulty(r.highestUserObservations.TotalDifficulty, head.TotalDifficulty) } select { - case <-requestCh: // no need to update latestChainInfo, as rpcClient already started new life cycle + case <-requestCh: // no need to update latestChainInfo, as RPCClient already started new life cycle return default: r.latestChainInfo.BlockNumber = head.Number @@ -1511,7 +1387,7 @@ func (r *rpcClient) onNewHead(ctx context.Context, requestCh <-chan struct{}, he } } -func (r *rpcClient) onNewFinalizedHead(ctx context.Context, requestCh <-chan struct{}, head *evmtypes.Head) { +func (r *RPCClient) onNewFinalizedHead(ctx context.Context, requestCh <-chan struct{}, head *evmtypes.Head) { if head == nil { return } @@ -1521,16 +1397,16 @@ func (r *rpcClient) onNewFinalizedHead(ctx context.Context, requestCh <-chan str r.highestUserObservations.FinalizedBlockNumber = max(r.highestUserObservations.FinalizedBlockNumber, head.Number) } select { - case <-requestCh: // no need to update latestChainInfo, as rpcClient already started new life cycle + case <-requestCh: // no need to update latestChainInfo, as RPCClient already started new life cycle return default: r.latestChainInfo.FinalizedBlockNumber = head.Number } } -func (r *rpcClient) GetInterceptedChainInfo() (latest, highestUserObservations commonclient.ChainInfo) { - r.chainInfoLock.RLock() - defer r.chainInfoLock.RUnlock() +func (r *RPCClient) GetInterceptedChainInfo() (latest, highestUserObservations commonclient.ChainInfo) { + r.chainInfoLock.Lock() + defer r.chainInfoLock.Unlock() return r.latestChainInfo, r.highestUserObservations } diff --git a/core/chains/evm/client/rpc_client_test.go b/core/chains/evm/client/rpc_client_test.go index 662c757ffb3..edbb10cc36f 100644 --- a/core/chains/evm/client/rpc_client_test.go +++ b/core/chains/evm/client/rpc_client_test.go @@ -19,13 +19,10 @@ import ( "github.com/tidwall/gjson" "go.uber.org/zap" - commontypes "github.com/smartcontractkit/chainlink/v2/common/types" - - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" + commontypes "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/testutils" @@ -40,7 +37,7 @@ func makeNewHeadWSMessage(head *evmtypes.Head) string { return fmt.Sprintf(`{"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x00","result":%s}}`, string(asJSON)) } -func TestRPCClient_SubscribeNewHead(t *testing.T) { +func TestRPCClient_SubscribeToHeads(t *testing.T) { t.Parallel() ctx, cancel := context.WithTimeout(tests.Context(t), tests.WaitTimeout(t)) defer cancel() @@ -48,24 +45,53 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { chainId := big.NewInt(123456) lggr := logger.Test(t) + nodePoolCfgHeadPolling := client.TestNodePoolConfig{ + NodeNewHeadsPollInterval: 1 * time.Second, + NodeFinalizedBlockPollInterval: 1 * time.Second, + } + + nodePoolCfgNoPolling := client.TestNodePoolConfig{ + NodeFinalizedBlockPollInterval: 1 * time.Second, + } + + var rpcHeads []*evmtypes.Head + previousHead := &evmtypes.Head{Number: 0} + SetNextRPCHead := func(head *evmtypes.Head) { + rpcHeads = append(rpcHeads, head) + } + serverCallBack := func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { if method == "eth_unsubscribe" { resp.Result = "true" return + } else if method == "eth_subscribe" { + assert.Equal(t, "eth_subscribe", method) + if assert.True(t, params.IsArray()) && assert.Equal(t, "newHeads", params.Array()[0].String()) { + resp.Result = `"0x00"` + } + return } - assert.Equal(t, "eth_subscribe", method) - if assert.True(t, params.IsArray()) && assert.Equal(t, "newHeads", params.Array()[0].String()) { - resp.Result = `"0x00"` + assert.Equal(t, "eth_getBlockByNumber", method) + if assert.True(t, params.IsArray()) && assert.Equal(t, "latest", params.Array()[0].String()) { + if len(rpcHeads) == 0 { + SetNextRPCHead(previousHead) + } + head := rpcHeads[0] + previousHead = head + rpcHeads = rpcHeads[1:] + jsonHead, err := json.Marshal(head) + if err != nil { + panic(fmt.Errorf("failed to marshal head: %w", err)) + } + resp.Result = string(jsonHead) } return } - checkClosedRPCClientShouldRemoveExistingSub := func(t tests.TestingT, ctx context.Context, sub commontypes.Subscription, rpcClient client.RPCClient) { + checkClosedRPCClientShouldRemoveExistingSub := func(t tests.TestingT, ctx context.Context, sub commontypes.Subscription, rpcClient *client.RPCClient) { errCh := sub.Err() - // ensure sub exists - require.Equal(t, int32(1), rpcClient.SubscribersCount()) - rpcClient.DisconnectAll() + rpcClient.UnsubscribeAllExcept() // ensure sub is closed select { @@ -75,13 +101,12 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { } require.NoError(t, rpcClient.Dial(ctx)) - require.Equal(t, int32(0), rpcClient.SubscribersCount()) } t.Run("WS and HTTP URL cannot be both empty", func(t *testing.T) { // ws is optional when LogBroadcaster is disabled, however SubscribeFilterLogs will return error if ws is missing observedLggr, _ := logger.TestObserved(t, zap.DebugLevel) - rpcClient := client.NewRPCClient(observedLggr, nil, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpcClient := client.NewRPCClient(nodePoolCfgHeadPolling, observedLggr, nil, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") require.Equal(t, errors.New("cannot dial rpc client when both ws and http info are missing"), rpcClient.Dial(ctx)) }) @@ -89,7 +114,7 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) // set to default values @@ -101,14 +126,14 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { assert.Equal(t, int64(0), highestUserObservations.FinalizedBlockNumber) assert.Nil(t, highestUserObservations.TotalDifficulty) - ch := make(chan *evmtypes.Head) - sub, err := rpc.SubscribeNewHead(tests.Context(t), ch) + SetNextRPCHead(&evmtypes.Head{Number: 256, TotalDifficulty: big.NewInt(1000)}) + SetNextRPCHead(&evmtypes.Head{Number: 128, TotalDifficulty: big.NewInt(500)}) + + ch, sub, err := rpc.SubscribeToHeads(tests.Context(t)) require.NoError(t, err) defer sub.Unsubscribe() - go server.MustWriteBinaryMessageSync(t, makeNewHeadWSMessage(&evmtypes.Head{Number: 256, TotalDifficulty: big.NewInt(1000)})) // received 256 head <-ch - go server.MustWriteBinaryMessageSync(t, makeNewHeadWSMessage(&evmtypes.Head{Number: 128, TotalDifficulty: big.NewInt(500)})) // received 128 head <-ch @@ -125,8 +150,8 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { assertHighestUserObservations(highestUserObservations) - // DisconnectAll resets latest - rpc.DisconnectAll() + // Close resets latest + rpc.Close() latest, highestUserObservations = rpc.GetInterceptedChainInfo() assert.Equal(t, int64(0), latest.BlockNumber) @@ -139,14 +164,15 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) - ch := make(chan *evmtypes.Head) - sub, err := rpc.SubscribeNewHead(commonclient.CtxAddHealthCheckFlag(tests.Context(t)), ch) + + SetNextRPCHead(&evmtypes.Head{Number: 256, TotalDifficulty: big.NewInt(1000)}) + + ch, sub, err := rpc.SubscribeToHeads(commonclient.CtxAddHealthCheckFlag(tests.Context(t))) require.NoError(t, err) defer sub.Unsubscribe() - go server.MustWriteBinaryMessageSync(t, makeNewHeadWSMessage(&evmtypes.Head{Number: 256, TotalDifficulty: big.NewInt(1000)})) // received 256 head <-ch @@ -155,66 +181,46 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { assert.Equal(t, int64(0), latest.FinalizedBlockNumber) assert.Equal(t, big.NewInt(1000), latest.TotalDifficulty) - assert.Equal(t, int64(0), highestUserObservations.BlockNumber) + assert.Equal(t, int64(256), highestUserObservations.BlockNumber) assert.Equal(t, int64(0), highestUserObservations.FinalizedBlockNumber) - assert.Equal(t, (*big.Int)(nil), highestUserObservations.TotalDifficulty) + assert.Equal(t, big.NewInt(1000), highestUserObservations.TotalDifficulty) }) t.Run("SubscribeToHeads with http polling enabled will update new heads", func(t *testing.T) { - type rpcServer struct { - Head *evmtypes.Head - URL *url.URL - } - createRPCServer := func() *rpcServer { - server := &rpcServer{} - server.Head = &evmtypes.Head{Number: 127} - server.URL = testutils.NewWSServer(t, chainId, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { - assert.Equal(t, "eth_getBlockByNumber", method) - if assert.True(t, params.IsArray()) && assert.Equal(t, "latest", params.Array()[0].String()) { - head := server.Head - jsonHead, err := json.Marshal(head) - if err != nil { - panic(fmt.Errorf("failed to marshal head: %w", err)) - } - resp.Result = string(jsonHead) - } - - return - }).WSURL() - return server - } + server := testutils.NewWSServer(t, chainId, serverCallBack) + wsURL := server.WSURL() - server := createRPCServer() - rpc := client.NewRPCClient(lggr, server.URL, nil, "rpc", 1, chainId, commonclient.Primary, 0, tests.TestInterval, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) + latest, highestUserObservations := rpc.GetInterceptedChainInfo() // latest chain info hasn't been initialized assert.Equal(t, int64(0), latest.BlockNumber) assert.Equal(t, int64(0), highestUserObservations.BlockNumber) + SetNextRPCHead(&evmtypes.Head{Number: 127, TotalDifficulty: big.NewInt(1000)}) + headCh, sub, err := rpc.SubscribeToHeads(commonclient.CtxAddHealthCheckFlag(tests.Context(t))) require.NoError(t, err) defer sub.Unsubscribe() head := <-headCh - assert.Equal(t, server.Head.Number, head.BlockNumber()) + assert.Equal(t, int64(127), head.BlockNumber()) // the http polling subscription should update the head block latest, highestUserObservations = rpc.GetInterceptedChainInfo() - assert.Equal(t, server.Head.Number, latest.BlockNumber) - assert.Equal(t, server.Head.Number, highestUserObservations.BlockNumber) + assert.Equal(t, int64(127), latest.BlockNumber) }) t.Run("Concurrent Unsubscribe and onNewHead calls do not lead to a deadlock", func(t *testing.T) { const numberOfAttempts = 1000 // need a large number to increase the odds of reproducing the issue server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) var wg sync.WaitGroup for i := 0; i < numberOfAttempts; i++ { - ch := make(chan *evmtypes.Head) - sub, err := rpc.SubscribeNewHead(tests.Context(t), ch) + _, sub, err := rpc.SubscribeToHeads(tests.Context(t)) require.NoError(t, err) wg.Add(2) go func() { @@ -222,7 +228,7 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { wg.Done() }() go func() { - rpc.UnsubscribeAllExceptAliveLoop() + rpc.UnsubscribeAllExcept(sub) sub.Unsubscribe() wg.Done() }() @@ -232,53 +238,49 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { t.Run("Block's chain ID matched configured", func(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) - ch := make(chan *evmtypes.Head) - sub, err := rpc.SubscribeNewHead(tests.Context(t), ch) + ch, sub, err := rpc.SubscribeToHeads(tests.Context(t)) require.NoError(t, err) defer sub.Unsubscribe() go server.MustWriteBinaryMessageSync(t, makeNewHeadWSMessage(&evmtypes.Head{Number: 256})) head := <-ch require.Equal(t, chainId, head.ChainID()) }) - t.Run("Failed SubscribeNewHead returns and logs proper error", func(t *testing.T) { + t.Run("Failed SubscribeToHeads returns and logs proper error", func(t *testing.T) { server := testutils.NewWSServer(t, chainId, func(reqMethod string, reqParams gjson.Result) (resp testutils.JSONRPCResponse) { return resp }) wsURL := server.WSURL() observedLggr, observed := logger.TestObserved(t, zap.DebugLevel) - rpc := client.NewRPCClient(observedLggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgNoPolling, observedLggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") require.NoError(t, rpc.Dial(ctx)) server.Close() - _, err := rpc.SubscribeNewHead(ctx, make(chan *evmtypes.Head)) + _, _, err := rpc.SubscribeToHeads(ctx) require.ErrorContains(t, err, "RPCClient returned error (rpc)") tests.AssertLogEventually(t, observed, "evmclient.Client#EthSubscribe RPC call failure") }) - t.Run("Closed rpc client should remove existing SubscribeNewHead subscription with WS", func(t *testing.T) { + t.Run("Closed rpc client should remove existing SubscribeToHeads subscription with WS", func(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - - rpc := client.NewRPCClient(lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgNoPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) - ch := make(chan *evmtypes.Head) - sub, err := rpc.SubscribeNewHead(tests.Context(t), ch) + _, sub, err := rpc.SubscribeToHeads(tests.Context(t)) require.NoError(t, err) checkClosedRPCClientShouldRemoveExistingSub(t, ctx, sub, rpc) }) - t.Run("Closed rpc client should remove existing SubscribeNewHead subscription with HTTP polling", func(t *testing.T) { + t.Run("Closed rpc client should remove existing SubscribeToHeads subscription with HTTP polling", func(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(lggr, wsURL, &url.URL{}, "rpc", 1, chainId, commonclient.Primary, 0, 1, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) - ch := make(chan *evmtypes.Head) - sub, err := rpc.SubscribeNewHead(tests.Context(t), ch) + _, sub, err := rpc.SubscribeToHeads(tests.Context(t)) require.NoError(t, err) checkClosedRPCClientShouldRemoveExistingSub(t, ctx, sub, rpc) }) @@ -286,7 +288,7 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgNoPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) @@ -298,7 +300,7 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(lggr, wsURL, &url.URL{}, "rpc", 1, chainId, commonclient.Primary, 0, 1, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) @@ -310,7 +312,7 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(lggr, wsURL, &url.URL{}, "rpc", 1, chainId, commonclient.Primary, 1, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgHeadPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) @@ -321,12 +323,14 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { t.Run("Subscription error is properly wrapper", func(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() - rpc := client.NewRPCClient(lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfgNoPolling, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) - sub, err := rpc.SubscribeNewHead(ctx, make(chan *evmtypes.Head)) + SetNextRPCHead(nil) + _, sub, err := rpc.SubscribeToHeads(ctx) require.NoError(t, err) go server.MustWriteBinaryMessageSync(t, "invalid msg") + select { case err = <-sub.Err(): require.ErrorContains(t, err, "RPCClient returned error (rpc): invalid character") @@ -339,6 +343,11 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { func TestRPCClient_SubscribeFilterLogs(t *testing.T) { t.Parallel() + nodePoolCfg := client.TestNodePoolConfig{ + NodeNewHeadsPollInterval: 1 * time.Second, + NodeFinalizedBlockPollInterval: 1 * time.Second, + } + chainId := big.NewInt(123456) lggr := logger.Test(t) ctx, cancel := context.WithTimeout(tests.Context(t), tests.WaitTimeout(t)) @@ -346,7 +355,7 @@ func TestRPCClient_SubscribeFilterLogs(t *testing.T) { t.Run("Failed SubscribeFilterLogs when WSURL is empty", func(t *testing.T) { // ws is optional when LogBroadcaster is disabled, however SubscribeFilterLogs will return error if ws is missing observedLggr, _ := logger.TestObserved(t, zap.DebugLevel) - rpcClient := client.NewRPCClient(observedLggr, nil, &url.URL{}, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpcClient := client.NewRPCClient(nodePoolCfg, observedLggr, nil, &url.URL{}, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") require.Nil(t, rpcClient.Dial(ctx)) _, err := rpcClient.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, make(chan types.Log)) @@ -358,7 +367,7 @@ func TestRPCClient_SubscribeFilterLogs(t *testing.T) { }) wsURL := server.WSURL() observedLggr, observed := logger.TestObserved(t, zap.DebugLevel) - rpc := client.NewRPCClient(observedLggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfg, observedLggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") require.NoError(t, rpc.Dial(ctx)) server.Close() _, err := rpc.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, make(chan types.Log)) @@ -375,7 +384,7 @@ func TestRPCClient_SubscribeFilterLogs(t *testing.T) { return resp }) wsURL := server.WSURL() - rpc := client.NewRPCClient(lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfg, lggr, wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) sub, err := rpc.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, make(chan types.Log)) @@ -397,6 +406,11 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) { ctx, cancel := context.WithTimeout(tests.Context(t), tests.WaitTimeout(t)) defer cancel() + nodePoolCfg := client.TestNodePoolConfig{ + NodeNewHeadsPollInterval: 1 * time.Second, + NodeFinalizedBlockPollInterval: 1 * time.Second, + } + chainId := big.NewInt(123456) lggr := logger.Test(t) @@ -424,7 +438,7 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) { } server := createRPCServer() - rpc := client.NewRPCClient(lggr, server.URL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + rpc := client.NewRPCClient(nodePoolCfg, lggr, server.URL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "") require.NoError(t, rpc.Dial(ctx)) defer rpc.Close() server.Head = &evmtypes.Head{Number: 128} @@ -463,8 +477,8 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) { assert.Equal(t, int64(0), latest.BlockNumber) assert.Equal(t, int64(256), latest.FinalizedBlockNumber) - // DisconnectAll resets latest ChainInfo - rpc.DisconnectAll() + // Close resets latest ChainInfo + rpc.Close() latest, highestUserObservations = rpc.GetInterceptedChainInfo() assert.Equal(t, int64(0), highestUserObservations.BlockNumber) assert.Equal(t, int64(128), highestUserObservations.FinalizedBlockNumber) @@ -476,40 +490,45 @@ func TestRPCClient_LatestFinalizedBlock(t *testing.T) { func TestRpcClientLargePayloadTimeout(t *testing.T) { t.Parallel() + nodePoolCfg := client.TestNodePoolConfig{ + NodeNewHeadsPollInterval: 1 * time.Second, + NodeFinalizedBlockPollInterval: 1 * time.Second, + } + testCases := []struct { Name string - Fn func(ctx context.Context, rpc client.RPCClient) error + Fn func(ctx context.Context, rpc *client.RPCClient) error }{ { Name: "SendTransaction", - Fn: func(ctx context.Context, rpc client.RPCClient) error { + Fn: func(ctx context.Context, rpc *client.RPCClient) error { return rpc.SendTransaction(ctx, types.NewTx(&types.LegacyTx{})) }, }, { Name: "EstimateGas", - Fn: func(ctx context.Context, rpc client.RPCClient) error { + Fn: func(ctx context.Context, rpc *client.RPCClient) error { _, err := rpc.EstimateGas(ctx, ethereum.CallMsg{}) return err }, }, { Name: "CallContract", - Fn: func(ctx context.Context, rpc client.RPCClient) error { + Fn: func(ctx context.Context, rpc *client.RPCClient) error { _, err := rpc.CallContract(ctx, ethereum.CallMsg{}, nil) return err }, }, { Name: "CallContext", - Fn: func(ctx context.Context, rpc client.RPCClient) error { + Fn: func(ctx context.Context, rpc *client.RPCClient) error { err := rpc.CallContext(ctx, nil, "rpc_call", nil) return err }, }, { Name: "BatchCallContext", - Fn: func(ctx context.Context, rpc client.RPCClient) error { + Fn: func(ctx context.Context, rpc *client.RPCClient) error { err := rpc.BatchCallContext(ctx, nil) return err }, @@ -534,7 +553,7 @@ func TestRpcClientLargePayloadTimeout(t *testing.T) { // use something unreasonably large for RPC timeout to ensure that we use largePayloadRPCTimeout const rpcTimeout = time.Hour const largePayloadRPCTimeout = tests.TestInterval - rpc := client.NewRPCClient(logger.Test(t), rpcURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, largePayloadRPCTimeout, rpcTimeout, "") + rpc := client.NewRPCClient(nodePoolCfg, logger.Test(t), rpcURL, nil, "rpc", 1, chainId, commonclient.Primary, largePayloadRPCTimeout, rpcTimeout, "") require.NoError(t, rpc.Dial(ctx)) defer rpc.Close() err := testCase.Fn(ctx, rpc) @@ -546,6 +565,11 @@ func TestRpcClientLargePayloadTimeout(t *testing.T) { func TestAstarCustomFinality(t *testing.T) { t.Parallel() + nodePoolCfg := client.TestNodePoolConfig{ + NodeNewHeadsPollInterval: 1 * time.Second, + NodeFinalizedBlockPollInterval: 1 * time.Second, + } + chainId := big.NewInt(123456) // create new server that returns 4 block for Astar custom finality and 8 block for finality tag. wsURL := testutils.NewWSServer(t, chainId, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { @@ -574,7 +598,7 @@ func TestAstarCustomFinality(t *testing.T) { const expectedFinalizedBlockNumber = int64(4) const expectedFinalizedBlockHash = "0x7441e97acf83f555e0deefef86db636bc8a37eb84747603412884e4df4d22804" - rpcClient := client.NewRPCClient(logger.Test(t), wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainAstar) + rpcClient := client.NewRPCClient(nodePoolCfg, logger.Test(t), wsURL, nil, "rpc", 1, chainId, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainAstar) defer rpcClient.Close() err := rpcClient.Dial(tests.Context(t)) require.NoError(t, err) diff --git a/core/chains/evm/client/simulated_backend_client.go b/core/chains/evm/client/simulated_backend_client.go index 11828e58710..3753a6704a5 100644 --- a/core/chains/evm/client/simulated_backend_client.go +++ b/core/chains/evm/client/simulated_backend_client.go @@ -297,20 +297,20 @@ func (h *headSubscription) Unsubscribe() { // Err returns err channel func (h *headSubscription) Err() <-chan error { return h.subscription.Err() } -// SubscribeNewHead registers a subscription for push notifications of new blocks. +// SubscribeToHeads registers a subscription for push notifications of new blocks. // Note the sim's API only accepts types.Head so we have this goroutine // to convert those into evmtypes.Head. -func (c *SimulatedBackendClient) SubscribeNewHead( +func (c *SimulatedBackendClient) SubscribeToHeads( ctx context.Context, - channel chan<- *evmtypes.Head, -) (ethereum.Subscription, error) { +) (<-chan *evmtypes.Head, ethereum.Subscription, error) { subscription := &headSubscription{unSub: make(chan chan struct{})} ch := make(chan *types.Header) + channel := make(chan *evmtypes.Head) var err error subscription.subscription, err = c.b.SubscribeNewHead(ctx, ch) if err != nil { - return nil, fmt.Errorf("%w: could not subscribe to new heads on "+ + return nil, nil, fmt.Errorf("%w: could not subscribe to new heads on "+ "simulated backend", err) } go func() { @@ -346,7 +346,7 @@ func (c *SimulatedBackendClient) SubscribeNewHead( } } }() - return subscription, err + return channel, subscription, err } // HeaderByNumber returns the geth header type. diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index 27a279f49fc..dff73cacb47 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -338,6 +338,7 @@ func (c *EVMConfig) ValidateConfig() (err error) { } err = multierr.Append(err, c.Chain.ValidateConfig()) + err = multierr.Append(err, c.NodePool.ValidateConfig(c.Chain.FinalityTagEnabled)) return } @@ -968,6 +969,20 @@ func (p *NodePool) setFrom(f *NodePool) { p.Errors.setFrom(&f.Errors) } +func (p *NodePool) ValidateConfig(finalityTagEnabled *bool) (err error) { + if finalityTagEnabled != nil && *finalityTagEnabled { + if p.FinalizedBlockPollInterval == nil { + err = multierr.Append(err, commonconfig.ErrMissing{Name: "FinalizedBlockPollInterval", Msg: "required when FinalityTagEnabled is true"}) + return + } + if p.FinalizedBlockPollInterval.Duration() <= 0 { + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "FinalizedBlockPollInterval", Value: p.FinalizedBlockPollInterval, + Msg: "must be greater than 0"}) + } + } + return +} + type OCR struct { ContractConfirmations *uint16 ContractTransmitterTransmitTimeout *commonconfig.Duration diff --git a/core/chains/evm/headtracker/head_broadcaster_test.go b/core/chains/evm/headtracker/head_broadcaster_test.go index 7ac61ab34b0..3cd02b3bf05 100644 --- a/core/chains/evm/headtracker/head_broadcaster_test.go +++ b/core/chains/evm/headtracker/head_broadcaster_test.go @@ -55,11 +55,12 @@ func TestHeadBroadcaster_Subscribe(t *testing.T) { ethClient := testutils.NewEthClientMockWithDefaultChain(t) chchHeaders := make(chan chan<- *evmtypes.Head, 1) - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + chHead := make(chan *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything). Run(func(args mock.Arguments) { - chchHeaders <- args.Get(1).(chan<- *evmtypes.Head) + chchHeaders <- chHead }). - Return(sub, nil) + Return((<-chan *evmtypes.Head)(chHead), sub, nil) ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(1), nil) sub.On("Unsubscribe").Return() diff --git a/core/chains/evm/headtracker/head_listener_test.go b/core/chains/evm/headtracker/head_listener_test.go index 2e459af2a2b..25902fa6b1a 100644 --- a/core/chains/evm/headtracker/head_listener_test.go +++ b/core/chains/evm/headtracker/head_listener_test.go @@ -27,7 +27,7 @@ func Test_HeadListener_HappyPath(t *testing.T) { t.Parallel() // Logic: // - spawn a listener instance - // - mock SubscribeNewHead/Err/Unsubscribe to track these calls + // - mock SubscribeToHeads/Err/Unsubscribe to track these calls // - send 3 heads // - ask listener to stop // Asserts: @@ -43,13 +43,12 @@ func Test_HeadListener_HappyPath(t *testing.T) { var headCount atomic.Int32 unsubscribeAwaiter := testutils.NewAwaiter() + chHeads := make(chan *evmtypes.Head) subscribeAwaiter := testutils.NewAwaiter() - var chHeads chan<- *evmtypes.Head var chErr = make(chan error) var chSubErr <-chan error = chErr sub := commonmocks.NewSubscription(t) - ethClient.On("SubscribeNewHead", mock.Anything, mock.AnythingOfType("chan<- *types.Head")).Return(sub, nil).Once().Run(func(args mock.Arguments) { - chHeads = args.Get(1).(chan<- *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything).Return((<-chan *evmtypes.Head)(chHeads), sub, nil).Once().Run(func(args mock.Arguments) { subscribeAwaiter.ItHappened() }) sub.On("Err").Return(chSubErr) @@ -97,13 +96,12 @@ func Test_HeadListener_NotReceivingHeads(t *testing.T) { firstHeadAwaiter := testutils.NewAwaiter() + chHeads := make(chan *evmtypes.Head) subscribeAwaiter := testutils.NewAwaiter() - var chHeads chan<- *evmtypes.Head var chErr = make(chan error) var chSubErr <-chan error = chErr sub := commonmocks.NewSubscription(t) - ethClient.On("SubscribeNewHead", mock.Anything, mock.AnythingOfType("chan<- *types.Head")).Return(sub, nil).Once().Run(func(args mock.Arguments) { - chHeads = args.Get(1).(chan<- *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything).Return((<-chan *evmtypes.Head)(chHeads), sub, nil).Once().Run(func(args mock.Arguments) { subscribeAwaiter.ItHappened() }) sub.On("Err").Return(chSubErr) @@ -161,11 +159,10 @@ func Test_HeadListener_SubscriptionErr(t *testing.T) { // initially and once again after exactly one head has been received sub.On("Err").Return(chSubErr).Twice() + headsCh := make(chan *evmtypes.Head) subscribeAwaiter := testutils.NewAwaiter() - var headsCh chan<- *evmtypes.Head // Initial subscribe - ethClient.On("SubscribeNewHead", mock.Anything, mock.AnythingOfType("chan<- *types.Head")).Return(sub, nil).Once().Run(func(args mock.Arguments) { - headsCh = args.Get(1).(chan<- *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything).Return((<-chan *evmtypes.Head)(headsCh), sub, nil).Once().Run(func(args mock.Arguments) { subscribeAwaiter.ItHappened() }) func() { @@ -199,9 +196,8 @@ func Test_HeadListener_SubscriptionErr(t *testing.T) { sub2.On("Err").Return(chSubErr2) subscribeAwaiter2 := testutils.NewAwaiter() - var headsCh2 chan<- *evmtypes.Head - ethClient.On("SubscribeNewHead", mock.Anything, mock.AnythingOfType("chan<- *types.Head")).Return(sub2, nil).Once().Run(func(args mock.Arguments) { - headsCh2 = args.Get(1).(chan<- *evmtypes.Head) + headsCh2 := make(chan *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything).Return((<-chan *evmtypes.Head)(headsCh2), sub2, nil).Once().Run(func(args mock.Arguments) { subscribeAwaiter2.ItHappened() }) diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index 5534a6aa10c..e357ff3a98f 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -63,9 +63,9 @@ func TestHeadTracker_New(t *testing.T) { mockEth := &testutils.MockEth{ EthClient: ethClient, } - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + ethClient.On("SubscribeToHeads", mock.Anything, mock.Anything). Maybe(). - Return(mockEth.NewSub(t), nil) + Return(nil, mockEth.NewSub(t), nil) orm := headtracker.NewORM(*testutils.FixtureChainID, db) assert.Nil(t, orm.IdempotentInsertHead(tests.Context(t), testutils.Head(1))) @@ -145,14 +145,13 @@ func TestHeadTracker_Get(t *testing.T) { mockEth := &testutils.MockEth{ EthClient: ethClient, } - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + ethClient.On("SubscribeToHeads", mock.Anything). Maybe(). Return( - func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { + func(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { defer close(chStarted) - return mockEth.NewSub(t) + return make(<-chan *evmtypes.Head), mockEth.NewSub(t), nil }, - func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(testutils.Head(0), nil).Maybe() @@ -198,11 +197,12 @@ func TestHeadTracker_Start_NewHeads(t *testing.T) { ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(0), nil).Once() // for backfill ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(0), nil).Maybe() - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + ch := make(chan *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything). Run(func(mock.Arguments) { close(chStarted) }). - Return(sub, nil) + Return((<-chan *evmtypes.Head)(ch), sub, nil) ht := createHeadTracker(t, ethClient, config.EVM(), config.EVM().HeadTracker(), orm) ht.Start(t) @@ -243,7 +243,7 @@ func TestHeadTracker_Start(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) mockEth := &testutils.MockEth{EthClient: ethClient} sub := mockEth.NewSub(t) - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(sub, nil).Maybe() + ethClient.On("SubscribeToHeads", mock.Anything, mock.Anything).Return(nil, sub, nil).Maybe() return createHeadTracker(t, ethClient, config.EVM(), config.EVM().HeadTracker(), opts.ORM) } t.Run("Starts even if failed to get initialHead", func(t *testing.T) { @@ -271,7 +271,7 @@ func TestHeadTracker_Start(t *testing.T) { head := testutils.Head(1000) ht.ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(head, nil).Once() ht.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(nil, nil).Once() - ht.ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(nil, errors.New("failed to connect")).Maybe() + ht.ethClient.On("SubscribeToHeads", mock.Anything, mock.Anything).Return(nil, nil, errors.New("failed to connect")).Maybe() ht.Start(t) tests.AssertLogEventually(t, ht.observer, "Error handling initial head") }) @@ -286,7 +286,7 @@ func TestHeadTracker_Start(t *testing.T) { ht.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(finalizedHead, nil).Once() // on backfill ht.ethClient.On("LatestFinalizedBlock", mock.Anything).Return(nil, errors.New("backfill call to finalized failed")).Maybe() - ht.ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(nil, errors.New("failed to connect")).Maybe() + ht.ethClient.On("SubscribeToHeads", mock.Anything, mock.Anything).Return(nil, nil, errors.New("failed to connect")).Maybe() ht.Start(t) tests.AssertLogEventually(t, ht.observer, "Received new head") tests.AssertEventually(t, func() bool { @@ -304,7 +304,7 @@ func TestHeadTracker_Start(t *testing.T) { require.NoError(t, ht.orm.IdempotentInsertHead(ctx, testutils.Head(finalizedHead.Number-1))) // on backfill ht.ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(nil, errors.New("backfill call to finalized failed")).Maybe() - ht.ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(nil, errors.New("failed to connect")).Maybe() + ht.ethClient.On("SubscribeToHeads", mock.Anything, mock.Anything).Return(nil, nil, errors.New("failed to connect")).Maybe() ht.Start(t) tests.AssertLogEventually(t, ht.observer, "Received new head") tests.AssertEventually(t, func() bool { @@ -361,14 +361,14 @@ func TestHeadTracker_CallsHeadTrackableCallbacks(t *testing.T) { chchHeaders := make(chan testutils.RawSub[*evmtypes.Head], 1) mockEth := &testutils.MockEth{EthClient: ethClient} - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + chHead := make(chan *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything). Return( - func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { + func(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { sub := mockEth.NewSub(t) - chchHeaders <- testutils.NewRawSub(ch, sub.Err()) - return sub + chchHeaders <- testutils.NewRawSub(chHead, sub.Err()) + return chHead, sub, nil }, - func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(0), nil) ethClient.On("HeadByHash", mock.Anything, mock.Anything).Return(testutils.Head(0), nil).Maybe() @@ -397,16 +397,19 @@ func TestHeadTracker_ReconnectOnError(t *testing.T) { ethClient := testutils.NewEthClientMockWithDefaultChain(t) mockEth := &testutils.MockEth{EthClient: ethClient} - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + chHead := make(chan *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything). Return( - func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { return mockEth.NewSub(t) }, - func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, + func(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { + return chHead, mockEth.NewSub(t), nil + }, ) - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(nil, errors.New("cannot reconnect")) - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + ethClient.On("SubscribeToHeads", mock.Anything).Return((<-chan *evmtypes.Head)(chHead), nil, errors.New("cannot reconnect")) + ethClient.On("SubscribeToHeads", mock.Anything). Return( - func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { return mockEth.NewSub(t) }, - func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, + func(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { + return chHead, mockEth.NewSub(t), nil + }, ) ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(0), nil) checker := &mocks.MockHeadTrackable{} @@ -431,16 +434,16 @@ func TestHeadTracker_ResubscribeOnSubscriptionError(t *testing.T) { ethClient := testutils.NewEthClientMockWithDefaultChain(t) + ch := make(chan *evmtypes.Head) chchHeaders := make(chan testutils.RawSub[*evmtypes.Head], 1) mockEth := &testutils.MockEth{EthClient: ethClient} - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + ethClient.On("SubscribeToHeads", mock.Anything). Return( - func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { + func(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { sub := mockEth.NewSub(t) chchHeaders <- testutils.NewRawSub(ch, sub.Err()) - return sub + return ch, sub, nil }, - func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(testutils.Head(0), nil) ethClient.On("HeadByHash", mock.Anything, mock.Anything).Return(testutils.Head(0), nil).Maybe() @@ -496,14 +499,14 @@ func TestHeadTracker_Start_LoadsLatestChain(t *testing.T) { chchHeaders := make(chan testutils.RawSub[*evmtypes.Head], 1) mockEth := &testutils.MockEth{EthClient: ethClient} - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + ch := make(chan *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything). Return( - func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { + func(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { sub := mockEth.NewSub(t) chchHeaders <- testutils.NewRawSub(ch, sub.Err()) - return sub + return ch, sub, nil }, - func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) orm := headtracker.NewORM(*testutils.FixtureChainID, db) @@ -553,14 +556,14 @@ func TestHeadTracker_SwitchesToLongestChainWithHeadSamplingEnabled(t *testing.T) chchHeaders := make(chan testutils.RawSub[*evmtypes.Head], 1) mockEth := &testutils.MockEth{EthClient: ethClient} - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + chHead := make(chan *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything). Return( - func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { + func(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { sub := mockEth.NewSub(t) - chchHeaders <- testutils.NewRawSub(ch, sub.Err()) - return sub + chchHeaders <- testutils.NewRawSub(chHead, sub.Err()) + return chHead, sub, nil }, - func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) // --------------------- @@ -674,14 +677,14 @@ func TestHeadTracker_SwitchesToLongestChainWithHeadSamplingDisabled(t *testing.T chchHeaders := make(chan testutils.RawSub[*evmtypes.Head], 1) mockEth := &testutils.MockEth{EthClient: ethClient} - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + chHead := make(chan *evmtypes.Head) + ethClient.On("SubscribeToHeads", mock.Anything). Return( - func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { + func(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { sub := mockEth.NewSub(t) - chchHeaders <- testutils.NewRawSub(ch, sub.Err()) - return sub + chchHeaders <- testutils.NewRawSub(chHead, sub.Err()) + return chHead, sub, nil }, - func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) // --------------------- diff --git a/core/chains/evm/testutils/client.go b/core/chains/evm/testutils/client.go index 1e5523fbff9..dfa4653de1c 100644 --- a/core/chains/evm/testutils/client.go +++ b/core/chains/evm/testutils/client.go @@ -6,6 +6,7 @@ import ( "net/http" "net/http/httptest" "net/url" + "strings" "sync" "sync/atomic" "testing" @@ -146,14 +147,75 @@ func (ts *testWSServer) newWSHandler(chainID *big.Int, callback JSONRPCHandler) return } ts.t.Log("Received message", string(data)) + req := gjson.ParseBytes(data) - if !req.IsObject() { - if isSingleObjectArray := req.IsArray() && len(req.Array()) == 1; !isSingleObjectArray { - ts.t.Logf("Request must be object: %v", req.Type) + + if req.IsArray() { // Handle batch request + ts.t.Log("Received batch request") + responses := []string{} + for _, reqElem := range req.Array() { + m := reqElem.Get("method") + if m.Type != gjson.String { + ts.t.Logf("Method must be string: %v", m.Type) + continue + } + + var resp JSONRPCResponse + if chainID != nil && m.String() == "eth_chainId" { + resp.Result = `"0x` + chainID.Text(16) + `"` + } else if m.String() == "eth_syncing" { + resp.Result = "false" + } else { + resp = callback(m.String(), reqElem.Get("params")) + } + id := reqElem.Get("id") + var msg string + if resp.Error.Message != "" { + msg = fmt.Sprintf(`{"jsonrpc":"2.0","id":%s,"error":{"code":%d,"message":"%s"}}`, id, resp.Error.Code, resp.Error.Message) + } else { + msg = fmt.Sprintf(`{"jsonrpc":"2.0","id":%s,"result":%s}`, id, resp.Result) + } + responses = append(responses, msg) + } + responseBatch := fmt.Sprintf("[%s]", strings.Join(responses, ",")) + ts.t.Logf("Sending batch response: %v", responseBatch) + ts.mu.Lock() + err = conn.WriteMessage(websocket.BinaryMessage, []byte(responseBatch)) + ts.mu.Unlock() + if err != nil { + ts.t.Logf("Failed to write message: %v", err) + return + } + } else { // Handle single request + m := req.Get("method") + if m.Type != gjson.String { + ts.t.Logf("Method must be string: %v", m.Type) return } - req = req.Array()[0] + var resp JSONRPCResponse + if chainID != nil && m.String() == "eth_chainId" { + resp.Result = `"0x` + chainID.Text(16) + `"` + } else if m.String() == "eth_syncing" { + resp.Result = "false" + } else { + resp = callback(m.String(), req.Get("params")) + } + id := req.Get("id") + var msg string + if resp.Error.Message != "" { + msg = fmt.Sprintf(`{"jsonrpc":"2.0","id":%s,"error":{"code":%d,"message":"%s"}}`, id, resp.Error.Code, resp.Error.Message) + } else { + msg = fmt.Sprintf(`{"jsonrpc":"2.0","id":%s,"result":%s}`, id, resp.Result) + } + ts.t.Logf("Sending message: %v", msg) + ts.mu.Lock() + err = conn.WriteMessage(websocket.BinaryMessage, []byte(msg)) + ts.mu.Unlock() + if err != nil { + ts.t.Logf("Failed to write message: %v", err) + return + } } if e := req.Get("error"); e.Exists() { ts.t.Logf("Received jsonrpc error: %v", e) diff --git a/core/chains/legacyevm/chain.go b/core/chains/legacyevm/chain.go index d296c6e8551..277d6283690 100644 --- a/core/chains/legacyevm/chain.go +++ b/core/chains/legacyevm/chain.go @@ -195,7 +195,11 @@ func newChain(ctx context.Context, cfg *evmconfig.ChainScoped, nodes []*toml.Nod if !opts.AppConfig.EVMRPCEnabled() { client = evmclient.NewNullClient(chainID, l) } else if opts.GenEthClient == nil { - client = evmclient.NewEvmClient(cfg.EVM().NodePool(), cfg.EVM(), cfg.EVM().NodePool().Errors(), l, chainID, nodes, cfg.EVM().ChainType()) + var err error + client, err = evmclient.NewEvmClient(cfg.EVM().NodePool(), cfg.EVM(), cfg.EVM().NodePool().Errors(), l, chainID, nodes, cfg.EVM().ChainType()) + if err != nil { + return nil, err + } } else { client = opts.GenEthClient(chainID) } @@ -425,7 +429,6 @@ func (c *chain) listNodeStatuses(start, end int) ([]types.NodeStatus, int, error for _, n := range nodes[start:end] { var ( nodeState string - exists bool ) toml, err := gotoml.Marshal(n) if err != nil { @@ -434,10 +437,11 @@ func (c *chain) listNodeStatuses(start, end int) ([]types.NodeStatus, int, error if states == nil { nodeState = "Unknown" } else { - nodeState, exists = states[*n.Name] - if !exists { - // The node is in the DB and the chain is enabled but it's not running - nodeState = "NotLoaded" + // The node is in the DB and the chain is enabled but it's not running + nodeState = "NotLoaded" + s, exists := states[*n.Name] + if exists { + nodeState = s } } stats = append(stats, types.NodeStatus{ diff --git a/core/gethwrappers/ccip/generated/ccip_reader_tester/ccip_reader_tester.go b/core/gethwrappers/ccip/generated/ccip_reader_tester/ccip_reader_tester.go deleted file mode 100644 index 1493e11623f..00000000000 --- a/core/gethwrappers/ccip/generated/ccip_reader_tester/ccip_reader_tester.go +++ /dev/null @@ -1,845 +0,0 @@ -// Code generated - DO NOT EDIT. -// This file is a generated binding and any manual changes will be lost. - -package ccip_reader_tester - -import ( - "errors" - "fmt" - "math/big" - "strings" - - ethereum "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/event" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" -) - -var ( - _ = errors.New - _ = big.NewInt - _ = strings.NewReader - _ = ethereum.NotFound - _ = bind.Bind - _ = common.Big1 - _ = types.BloomLookup - _ = event.NewSubscription - _ = abi.ConvertType -) - -type IRMNRemoteSignature struct { - R [32]byte - S [32]byte -} - -type InternalEVM2AnyRampMessage struct { - Header InternalRampMessageHeader - Sender common.Address - Data []byte - Receiver []byte - ExtraArgs []byte - FeeToken common.Address - FeeTokenAmount *big.Int - FeeValueJuels *big.Int - TokenAmounts []InternalEVM2AnyTokenTransfer -} - -type InternalEVM2AnyTokenTransfer struct { - SourcePoolAddress common.Address - DestTokenAddress []byte - ExtraData []byte - Amount *big.Int - DestExecData []byte -} - -type InternalGasPriceUpdate struct { - DestChainSelector uint64 - UsdPerUnitGas *big.Int -} - -type InternalMerkleRoot struct { - SourceChainSelector uint64 - OnRampAddress []byte - MinSeqNr uint64 - MaxSeqNr uint64 - MerkleRoot [32]byte -} - -type InternalPriceUpdates struct { - TokenPriceUpdates []InternalTokenPriceUpdate - GasPriceUpdates []InternalGasPriceUpdate -} - -type InternalRampMessageHeader struct { - MessageId [32]byte - SourceChainSelector uint64 - DestChainSelector uint64 - SequenceNumber uint64 - Nonce uint64 -} - -type InternalTokenPriceUpdate struct { - SourceToken common.Address - UsdPerToken *big.Int -} - -type OffRampCommitReport struct { - PriceUpdates InternalPriceUpdates - MerkleRoots []InternalMerkleRoot - RmnSignatures []IRMNRemoteSignature - RmnRawVs *big.Int -} - -type OffRampSourceChainConfig struct { - Router common.Address - IsEnabled bool - MinSeqNr uint64 - OnRamp []byte -} - -var CCIPReaderTesterMetaData = &bind.MetaData{ - ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"feeValueJuels\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"sourcePoolAddress\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"destExecData\",\"type\":\"bytes\"}],\"internalType\":\"structInternal.EVM2AnyTokenTransfer[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"}],\"indexed\":false,\"internalType\":\"structInternal.EVM2AnyRampMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"CCIPMessageSent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRampAddress\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"maxSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"indexed\":false,\"internalType\":\"structInternal.MerkleRoot[]\",\"name\":\"merkleRoots\",\"type\":\"tuple[]\"},{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sourceToken\",\"type\":\"address\"},{\"internalType\":\"uint224\",\"name\":\"usdPerToken\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.TokenPriceUpdate[]\",\"name\":\"tokenPriceUpdates\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint224\",\"name\":\"usdPerUnitGas\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.GasPriceUpdate[]\",\"name\":\"gasPriceUpdates\",\"type\":\"tuple[]\"}],\"indexed\":false,\"internalType\":\"structInternal.PriceUpdates\",\"name\":\"priceUpdates\",\"type\":\"tuple\"}],\"name\":\"CommitReportAccepted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"state\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"name\":\"ExecutionStateChanged\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"feeValueJuels\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"sourcePoolAddress\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"destExecData\",\"type\":\"bytes\"}],\"internalType\":\"structInternal.EVM2AnyTokenTransfer[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.EVM2AnyRampMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"emitCCIPMessageSent\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sourceToken\",\"type\":\"address\"},{\"internalType\":\"uint224\",\"name\":\"usdPerToken\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.TokenPriceUpdate[]\",\"name\":\"tokenPriceUpdates\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint224\",\"name\":\"usdPerUnitGas\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.GasPriceUpdate[]\",\"name\":\"gasPriceUpdates\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.PriceUpdates\",\"name\":\"priceUpdates\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRampAddress\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"maxSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structInternal.MerkleRoot[]\",\"name\":\"merkleRoots\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"internalType\":\"structIRMNRemote.Signature[]\",\"name\":\"rmnSignatures\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256\",\"name\":\"rmnRawVs\",\"type\":\"uint256\"}],\"internalType\":\"structOffRamp.CommitReport\",\"name\":\"report\",\"type\":\"tuple\"}],\"name\":\"emitCommitReportAccepted\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"state\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"name\":\"emitExecutionStateChanged\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"getExpectedNextSequenceNumber\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"}],\"name\":\"getInboundNonce\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"getSourceChainConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"contractIRouter\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structOffRamp.SourceChainConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"setDestChainSeqNr\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"testNonce\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"}],\"name\":\"setInboundNonce\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"contractIRouter\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structOffRamp.SourceChainConfig\",\"name\":\"sourceChainConfig\",\"type\":\"tuple\"}],\"name\":\"setSourceChainConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x608060405234801561001057600080fd5b5061188a806100206000396000f3fe608060405234801561001057600080fd5b50600436106100a35760003560e01c806393df286711610076578063c92236251161005b578063c92236251461017c578063e83eabba1461018f578063e9d68a8e146101a257600080fd5b806393df286714610114578063c1a5a3551461012757600080fd5b8063198b821c146100a85780634bf78697146100bd5780634cf66e36146100d05780639041be3d146100e3575b600080fd5b6100bb6100b6366004610a34565b6101c2565b005b6100bb6100cb366004610d7a565b610204565b6100bb6100de366004610eb5565b61024a565b6100f66100f1366004610f34565b6102a1565b60405167ffffffffffffffff90911681526020015b60405180910390f35b6100bb610122366004610f9f565b6102d1565b6100bb610135366004611000565b67ffffffffffffffff918216600090815260016020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001691909216179055565b6100f661018a366004611033565b61034c565b6100bb61019d366004611086565b610398565b6101b56101b0366004610f34565b610482565b60405161010b91906111a6565b602081015181516040517f35c02761bcd3ef995c6a601a1981f4ed3934dcbe5041e24e286c89f5531d17e4926101f99290916112fd565b60405180910390a150565b8167ffffffffffffffff167f8cd775d4a25bd349439a70817fd110144d6ab229ae1b9f54a1e5777d2041bfed8260405161023e91906114b8565b60405180910390a25050565b828467ffffffffffffffff168667ffffffffffffffff167f8c324ce1367b83031769f6a813e3bb4c117aba2185789d66b98b791405be6df28585604051610292929190611610565b60405180910390a45050505050565b67ffffffffffffffff808216600090815260016020819052604082205491926102cb921690611660565b92915050565b67ffffffffffffffff84166000908152600260205260409081902090518491906102fe90859085906116af565b908152604051908190036020019020805467ffffffffffffffff929092167fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000090921691909117905550505050565b67ffffffffffffffff8316600090815260026020526040808220905161037590859085906116af565b9081526040519081900360200190205467ffffffffffffffff1690509392505050565b67ffffffffffffffff808316600090815260208181526040918290208451815492860151938601519094167501000000000000000000000000000000000000000000027fffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffff93151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00000000000000000000000000000000000000000090931673ffffffffffffffffffffffffffffffffffffffff9095169490941791909117919091169190911781556060820151829190600182019061047b9082611763565b5050505050565b604080516080808201835260008083526020808401829052838501829052606080850181905267ffffffffffffffff87811684528383529286902086519485018752805473ffffffffffffffffffffffffffffffffffffffff8116865274010000000000000000000000000000000000000000810460ff16151593860193909352750100000000000000000000000000000000000000000090920490921694830194909452600184018054939492939184019161053e906116bf565b80601f016020809104026020016040519081016040528092919081815260200182805461056a906116bf565b80156105b75780601f1061058c576101008083540402835291602001916105b7565b820191906000526020600020905b81548152906001019060200180831161059a57829003601f168201915b5050505050815250509050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715610619576106196105c7565b60405290565b60405160a0810167ffffffffffffffff81118282101715610619576106196105c7565b6040516080810167ffffffffffffffff81118282101715610619576106196105c7565b604051610120810167ffffffffffffffff81118282101715610619576106196105c7565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156106d0576106d06105c7565b604052919050565b600067ffffffffffffffff8211156106f2576106f26105c7565b5060051b60200190565b73ffffffffffffffffffffffffffffffffffffffff8116811461071e57600080fd5b50565b803561072c816106fc565b919050565b80357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461072c57600080fd5b803567ffffffffffffffff8116811461072c57600080fd5b600082601f83011261078657600080fd5b8135602061079b610796836106d8565b610689565b82815260069290921b840181019181810190868411156107ba57600080fd5b8286015b8481101561080757604081890312156107d75760008081fd5b6107df6105f6565b6107e88261075d565b81526107f5858301610731565b818601528352918301916040016107be565b509695505050505050565b600082601f83011261082357600080fd5b813567ffffffffffffffff81111561083d5761083d6105c7565b61086e60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601610689565b81815284602083860101111561088357600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f8301126108b157600080fd5b813560206108c1610796836106d8565b82815260059290921b840181019181810190868411156108e057600080fd5b8286015b8481101561080757803567ffffffffffffffff808211156109055760008081fd5b818901915060a0807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848d0301121561093e5760008081fd5b61094661061f565b61095188850161075d565b8152604080850135848111156109675760008081fd5b6109758e8b83890101610812565b8a840152506060935061098984860161075d565b90820152608061099a85820161075d565b938201939093529201359082015283529183019183016108e4565b600082601f8301126109c657600080fd5b813560206109d6610796836106d8565b82815260069290921b840181019181810190868411156109f557600080fd5b8286015b848110156108075760408189031215610a125760008081fd5b610a1a6105f6565b8135815284820135858201528352918301916040016109f9565b60006020808385031215610a4757600080fd5b823567ffffffffffffffff80821115610a5f57600080fd5b9084019060808287031215610a7357600080fd5b610a7b610642565b823582811115610a8a57600080fd5b83016040818903811315610a9d57600080fd5b610aa56105f6565b823585811115610ab457600080fd5b8301601f81018b13610ac557600080fd5b8035610ad3610796826106d8565b81815260069190911b8201890190898101908d831115610af257600080fd5b928a01925b82841015610b425785848f031215610b0f5760008081fd5b610b176105f6565b8435610b22816106fc565b8152610b2f858d01610731565b818d0152825292850192908a0190610af7565b845250505082870135915084821115610b5a57600080fd5b610b668a838501610775565b81880152835250508284013582811115610b7f57600080fd5b610b8b888286016108a0565b85830152506040830135935081841115610ba457600080fd5b610bb0878585016109b5565b6040820152606083013560608201528094505050505092915050565b600060a08284031215610bde57600080fd5b610be661061f565b905081358152610bf86020830161075d565b6020820152610c096040830161075d565b6040820152610c1a6060830161075d565b6060820152610c2b6080830161075d565b608082015292915050565b600082601f830112610c4757600080fd5b81356020610c57610796836106d8565b82815260059290921b84018101918181019086841115610c7657600080fd5b8286015b8481101561080757803567ffffffffffffffff80821115610c9b5760008081fd5b818901915060a0807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848d03011215610cd45760008081fd5b610cdc61061f565b610ce7888501610721565b815260408085013584811115610cfd5760008081fd5b610d0b8e8b83890101610812565b8a8401525060608086013585811115610d245760008081fd5b610d328f8c838a0101610812565b83850152506080915081860135818401525082850135925083831115610d585760008081fd5b610d668d8a85880101610812565b908201528652505050918301918301610c7a565b60008060408385031215610d8d57600080fd5b610d968361075d565b9150602083013567ffffffffffffffff80821115610db357600080fd5b908401906101a08287031215610dc857600080fd5b610dd0610665565b610dda8784610bcc565b8152610de860a08401610721565b602082015260c083013582811115610dff57600080fd5b610e0b88828601610812565b60408301525060e083013582811115610e2357600080fd5b610e2f88828601610812565b6060830152506101008084013583811115610e4957600080fd5b610e5589828701610812565b608084015250610e686101208501610721565b60a083015261014084013560c083015261016084013560e083015261018084013583811115610e9657600080fd5b610ea289828701610c36565b8284015250508093505050509250929050565b600080600080600060a08688031215610ecd57600080fd5b610ed68661075d565b9450610ee46020870161075d565b935060408601359250606086013560048110610eff57600080fd5b9150608086013567ffffffffffffffff811115610f1b57600080fd5b610f2788828901610812565b9150509295509295909350565b600060208284031215610f4657600080fd5b610f4f8261075d565b9392505050565b60008083601f840112610f6857600080fd5b50813567ffffffffffffffff811115610f8057600080fd5b602083019150836020828501011115610f9857600080fd5b9250929050565b60008060008060608587031215610fb557600080fd5b610fbe8561075d565b9350610fcc6020860161075d565b9250604085013567ffffffffffffffff811115610fe857600080fd5b610ff487828801610f56565b95989497509550505050565b6000806040838503121561101357600080fd5b61101c8361075d565b915061102a6020840161075d565b90509250929050565b60008060006040848603121561104857600080fd5b6110518461075d565b9250602084013567ffffffffffffffff81111561106d57600080fd5b61107986828701610f56565b9497909650939450505050565b6000806040838503121561109957600080fd5b6110a28361075d565b9150602083013567ffffffffffffffff808211156110bf57600080fd5b90840190608082870312156110d357600080fd5b6110db610642565b82356110e6816106fc565b8152602083013580151581146110fb57600080fd5b602082015261110c6040840161075d565b604082015260608301358281111561112357600080fd5b61112f88828601610812565b6060830152508093505050509250929050565b6000815180845260005b818110156111685760208185018101518683018201520161114c565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815273ffffffffffffffffffffffffffffffffffffffff825116602082015260208201511515604082015267ffffffffffffffff60408301511660608201526000606083015160808084015261120160a0840182611142565b949350505050565b805160408084528151848201819052600092602091908201906060870190855b81811015611282578351805173ffffffffffffffffffffffffffffffffffffffff1684528501517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16858401529284019291850191600101611229565b50508583015187820388850152805180835290840192506000918401905b808310156112f1578351805167ffffffffffffffff1683528501517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16858301529284019260019290920191908501906112a0565b50979650505050505050565b60006040808301604084528086518083526060925060608601915060608160051b8701016020808a0160005b848110156113b5577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08a8503018652815160a067ffffffffffffffff80835116875285830151828789015261138083890182611142565b848d01518316898e01528b8501519092168b890152506080928301519290960191909152509482019490820190600101611329565b5050878203908801526113c88189611209565b9998505050505050505050565b600082825180855260208086019550808260051b84010181860160005b848110156114ab577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0868403018952815160a073ffffffffffffffffffffffffffffffffffffffff825116855285820151818787015261145482870182611142565b9150506040808301518683038288015261146e8382611142565b925050506060808301518187015250608080830151925085820381870152506114978183611142565b9a86019a94505050908301906001016113f2565b5090979650505050505050565b6020815261150960208201835180518252602081015167ffffffffffffffff808216602085015280604084015116604085015280606084015116606085015280608084015116608085015250505050565b6000602083015161153260c084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060408301516101a08060e085015261154f6101c0850183611142565b915060608501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061010081878603018188015261158d8584611142565b94506080880151925081878603016101208801526115ab8584611142565b945060a088015192506115d761014088018473ffffffffffffffffffffffffffffffffffffffff169052565b60c088015161016088015260e088015161018088015287015186850390910183870152905061160683826113d5565b9695505050505050565b600060048410611649577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b838252604060208301526112016040830184611142565b67ffffffffffffffff8181168382160190808211156116a8577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5092915050565b8183823760009101908152919050565b600181811c908216806116d357607f821691505b60208210810361170c577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f82111561175e576000816000526020600020601f850160051c8101602086101561173b5750805b601f850160051c820191505b8181101561175a57828155600101611747565b5050505b505050565b815167ffffffffffffffff81111561177d5761177d6105c7565b6117918161178b84546116bf565b84611712565b602080601f8311600181146117e457600084156117ae5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b17855561175a565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561183157888601518255948401946001909101908401611812565b508582101561186d57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b0190555056fea164736f6c6343000818000a", -} - -var CCIPReaderTesterABI = CCIPReaderTesterMetaData.ABI - -var CCIPReaderTesterBin = CCIPReaderTesterMetaData.Bin - -func DeployCCIPReaderTester(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *CCIPReaderTester, error) { - parsed, err := CCIPReaderTesterMetaData.GetAbi() - if err != nil { - return common.Address{}, nil, nil, err - } - if parsed == nil { - return common.Address{}, nil, nil, errors.New("GetABI returned nil") - } - - address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(CCIPReaderTesterBin), backend) - if err != nil { - return common.Address{}, nil, nil, err - } - return address, tx, &CCIPReaderTester{address: address, abi: *parsed, CCIPReaderTesterCaller: CCIPReaderTesterCaller{contract: contract}, CCIPReaderTesterTransactor: CCIPReaderTesterTransactor{contract: contract}, CCIPReaderTesterFilterer: CCIPReaderTesterFilterer{contract: contract}}, nil -} - -type CCIPReaderTester struct { - address common.Address - abi abi.ABI - CCIPReaderTesterCaller - CCIPReaderTesterTransactor - CCIPReaderTesterFilterer -} - -type CCIPReaderTesterCaller struct { - contract *bind.BoundContract -} - -type CCIPReaderTesterTransactor struct { - contract *bind.BoundContract -} - -type CCIPReaderTesterFilterer struct { - contract *bind.BoundContract -} - -type CCIPReaderTesterSession struct { - Contract *CCIPReaderTester - CallOpts bind.CallOpts - TransactOpts bind.TransactOpts -} - -type CCIPReaderTesterCallerSession struct { - Contract *CCIPReaderTesterCaller - CallOpts bind.CallOpts -} - -type CCIPReaderTesterTransactorSession struct { - Contract *CCIPReaderTesterTransactor - TransactOpts bind.TransactOpts -} - -type CCIPReaderTesterRaw struct { - Contract *CCIPReaderTester -} - -type CCIPReaderTesterCallerRaw struct { - Contract *CCIPReaderTesterCaller -} - -type CCIPReaderTesterTransactorRaw struct { - Contract *CCIPReaderTesterTransactor -} - -func NewCCIPReaderTester(address common.Address, backend bind.ContractBackend) (*CCIPReaderTester, error) { - abi, err := abi.JSON(strings.NewReader(CCIPReaderTesterABI)) - if err != nil { - return nil, err - } - contract, err := bindCCIPReaderTester(address, backend, backend, backend) - if err != nil { - return nil, err - } - return &CCIPReaderTester{address: address, abi: abi, CCIPReaderTesterCaller: CCIPReaderTesterCaller{contract: contract}, CCIPReaderTesterTransactor: CCIPReaderTesterTransactor{contract: contract}, CCIPReaderTesterFilterer: CCIPReaderTesterFilterer{contract: contract}}, nil -} - -func NewCCIPReaderTesterCaller(address common.Address, caller bind.ContractCaller) (*CCIPReaderTesterCaller, error) { - contract, err := bindCCIPReaderTester(address, caller, nil, nil) - if err != nil { - return nil, err - } - return &CCIPReaderTesterCaller{contract: contract}, nil -} - -func NewCCIPReaderTesterTransactor(address common.Address, transactor bind.ContractTransactor) (*CCIPReaderTesterTransactor, error) { - contract, err := bindCCIPReaderTester(address, nil, transactor, nil) - if err != nil { - return nil, err - } - return &CCIPReaderTesterTransactor{contract: contract}, nil -} - -func NewCCIPReaderTesterFilterer(address common.Address, filterer bind.ContractFilterer) (*CCIPReaderTesterFilterer, error) { - contract, err := bindCCIPReaderTester(address, nil, nil, filterer) - if err != nil { - return nil, err - } - return &CCIPReaderTesterFilterer{contract: contract}, nil -} - -func bindCCIPReaderTester(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := CCIPReaderTesterMetaData.GetAbi() - if err != nil { - return nil, err - } - return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil -} - -func (_CCIPReaderTester *CCIPReaderTesterRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { - return _CCIPReaderTester.Contract.CCIPReaderTesterCaller.contract.Call(opts, result, method, params...) -} - -func (_CCIPReaderTester *CCIPReaderTesterRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { - return _CCIPReaderTester.Contract.CCIPReaderTesterTransactor.contract.Transfer(opts) -} - -func (_CCIPReaderTester *CCIPReaderTesterRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { - return _CCIPReaderTester.Contract.CCIPReaderTesterTransactor.contract.Transact(opts, method, params...) -} - -func (_CCIPReaderTester *CCIPReaderTesterCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { - return _CCIPReaderTester.Contract.contract.Call(opts, result, method, params...) -} - -func (_CCIPReaderTester *CCIPReaderTesterTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { - return _CCIPReaderTester.Contract.contract.Transfer(opts) -} - -func (_CCIPReaderTester *CCIPReaderTesterTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { - return _CCIPReaderTester.Contract.contract.Transact(opts, method, params...) -} - -func (_CCIPReaderTester *CCIPReaderTesterCaller) GetExpectedNextSequenceNumber(opts *bind.CallOpts, destChainSelector uint64) (uint64, error) { - var out []interface{} - err := _CCIPReaderTester.contract.Call(opts, &out, "getExpectedNextSequenceNumber", destChainSelector) - - if err != nil { - return *new(uint64), err - } - - out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) - - return out0, err - -} - -func (_CCIPReaderTester *CCIPReaderTesterSession) GetExpectedNextSequenceNumber(destChainSelector uint64) (uint64, error) { - return _CCIPReaderTester.Contract.GetExpectedNextSequenceNumber(&_CCIPReaderTester.CallOpts, destChainSelector) -} - -func (_CCIPReaderTester *CCIPReaderTesterCallerSession) GetExpectedNextSequenceNumber(destChainSelector uint64) (uint64, error) { - return _CCIPReaderTester.Contract.GetExpectedNextSequenceNumber(&_CCIPReaderTester.CallOpts, destChainSelector) -} - -func (_CCIPReaderTester *CCIPReaderTesterCaller) GetInboundNonce(opts *bind.CallOpts, sourceChainSelector uint64, sender []byte) (uint64, error) { - var out []interface{} - err := _CCIPReaderTester.contract.Call(opts, &out, "getInboundNonce", sourceChainSelector, sender) - - if err != nil { - return *new(uint64), err - } - - out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) - - return out0, err - -} - -func (_CCIPReaderTester *CCIPReaderTesterSession) GetInboundNonce(sourceChainSelector uint64, sender []byte) (uint64, error) { - return _CCIPReaderTester.Contract.GetInboundNonce(&_CCIPReaderTester.CallOpts, sourceChainSelector, sender) -} - -func (_CCIPReaderTester *CCIPReaderTesterCallerSession) GetInboundNonce(sourceChainSelector uint64, sender []byte) (uint64, error) { - return _CCIPReaderTester.Contract.GetInboundNonce(&_CCIPReaderTester.CallOpts, sourceChainSelector, sender) -} - -func (_CCIPReaderTester *CCIPReaderTesterCaller) GetSourceChainConfig(opts *bind.CallOpts, sourceChainSelector uint64) (OffRampSourceChainConfig, error) { - var out []interface{} - err := _CCIPReaderTester.contract.Call(opts, &out, "getSourceChainConfig", sourceChainSelector) - - if err != nil { - return *new(OffRampSourceChainConfig), err - } - - out0 := *abi.ConvertType(out[0], new(OffRampSourceChainConfig)).(*OffRampSourceChainConfig) - - return out0, err - -} - -func (_CCIPReaderTester *CCIPReaderTesterSession) GetSourceChainConfig(sourceChainSelector uint64) (OffRampSourceChainConfig, error) { - return _CCIPReaderTester.Contract.GetSourceChainConfig(&_CCIPReaderTester.CallOpts, sourceChainSelector) -} - -func (_CCIPReaderTester *CCIPReaderTesterCallerSession) GetSourceChainConfig(sourceChainSelector uint64) (OffRampSourceChainConfig, error) { - return _CCIPReaderTester.Contract.GetSourceChainConfig(&_CCIPReaderTester.CallOpts, sourceChainSelector) -} - -func (_CCIPReaderTester *CCIPReaderTesterTransactor) EmitCCIPMessageSent(opts *bind.TransactOpts, destChainSelector uint64, message InternalEVM2AnyRampMessage) (*types.Transaction, error) { - return _CCIPReaderTester.contract.Transact(opts, "emitCCIPMessageSent", destChainSelector, message) -} - -func (_CCIPReaderTester *CCIPReaderTesterSession) EmitCCIPMessageSent(destChainSelector uint64, message InternalEVM2AnyRampMessage) (*types.Transaction, error) { - return _CCIPReaderTester.Contract.EmitCCIPMessageSent(&_CCIPReaderTester.TransactOpts, destChainSelector, message) -} - -func (_CCIPReaderTester *CCIPReaderTesterTransactorSession) EmitCCIPMessageSent(destChainSelector uint64, message InternalEVM2AnyRampMessage) (*types.Transaction, error) { - return _CCIPReaderTester.Contract.EmitCCIPMessageSent(&_CCIPReaderTester.TransactOpts, destChainSelector, message) -} - -func (_CCIPReaderTester *CCIPReaderTesterTransactor) EmitCommitReportAccepted(opts *bind.TransactOpts, report OffRampCommitReport) (*types.Transaction, error) { - return _CCIPReaderTester.contract.Transact(opts, "emitCommitReportAccepted", report) -} - -func (_CCIPReaderTester *CCIPReaderTesterSession) EmitCommitReportAccepted(report OffRampCommitReport) (*types.Transaction, error) { - return _CCIPReaderTester.Contract.EmitCommitReportAccepted(&_CCIPReaderTester.TransactOpts, report) -} - -func (_CCIPReaderTester *CCIPReaderTesterTransactorSession) EmitCommitReportAccepted(report OffRampCommitReport) (*types.Transaction, error) { - return _CCIPReaderTester.Contract.EmitCommitReportAccepted(&_CCIPReaderTester.TransactOpts, report) -} - -func (_CCIPReaderTester *CCIPReaderTesterTransactor) EmitExecutionStateChanged(opts *bind.TransactOpts, sourceChainSelector uint64, sequenceNumber uint64, messageId [32]byte, state uint8, returnData []byte) (*types.Transaction, error) { - return _CCIPReaderTester.contract.Transact(opts, "emitExecutionStateChanged", sourceChainSelector, sequenceNumber, messageId, state, returnData) -} - -func (_CCIPReaderTester *CCIPReaderTesterSession) EmitExecutionStateChanged(sourceChainSelector uint64, sequenceNumber uint64, messageId [32]byte, state uint8, returnData []byte) (*types.Transaction, error) { - return _CCIPReaderTester.Contract.EmitExecutionStateChanged(&_CCIPReaderTester.TransactOpts, sourceChainSelector, sequenceNumber, messageId, state, returnData) -} - -func (_CCIPReaderTester *CCIPReaderTesterTransactorSession) EmitExecutionStateChanged(sourceChainSelector uint64, sequenceNumber uint64, messageId [32]byte, state uint8, returnData []byte) (*types.Transaction, error) { - return _CCIPReaderTester.Contract.EmitExecutionStateChanged(&_CCIPReaderTester.TransactOpts, sourceChainSelector, sequenceNumber, messageId, state, returnData) -} - -func (_CCIPReaderTester *CCIPReaderTesterTransactor) SetDestChainSeqNr(opts *bind.TransactOpts, destChainSelector uint64, sequenceNumber uint64) (*types.Transaction, error) { - return _CCIPReaderTester.contract.Transact(opts, "setDestChainSeqNr", destChainSelector, sequenceNumber) -} - -func (_CCIPReaderTester *CCIPReaderTesterSession) SetDestChainSeqNr(destChainSelector uint64, sequenceNumber uint64) (*types.Transaction, error) { - return _CCIPReaderTester.Contract.SetDestChainSeqNr(&_CCIPReaderTester.TransactOpts, destChainSelector, sequenceNumber) -} - -func (_CCIPReaderTester *CCIPReaderTesterTransactorSession) SetDestChainSeqNr(destChainSelector uint64, sequenceNumber uint64) (*types.Transaction, error) { - return _CCIPReaderTester.Contract.SetDestChainSeqNr(&_CCIPReaderTester.TransactOpts, destChainSelector, sequenceNumber) -} - -func (_CCIPReaderTester *CCIPReaderTesterTransactor) SetInboundNonce(opts *bind.TransactOpts, sourceChainSelector uint64, testNonce uint64, sender []byte) (*types.Transaction, error) { - return _CCIPReaderTester.contract.Transact(opts, "setInboundNonce", sourceChainSelector, testNonce, sender) -} - -func (_CCIPReaderTester *CCIPReaderTesterSession) SetInboundNonce(sourceChainSelector uint64, testNonce uint64, sender []byte) (*types.Transaction, error) { - return _CCIPReaderTester.Contract.SetInboundNonce(&_CCIPReaderTester.TransactOpts, sourceChainSelector, testNonce, sender) -} - -func (_CCIPReaderTester *CCIPReaderTesterTransactorSession) SetInboundNonce(sourceChainSelector uint64, testNonce uint64, sender []byte) (*types.Transaction, error) { - return _CCIPReaderTester.Contract.SetInboundNonce(&_CCIPReaderTester.TransactOpts, sourceChainSelector, testNonce, sender) -} - -func (_CCIPReaderTester *CCIPReaderTesterTransactor) SetSourceChainConfig(opts *bind.TransactOpts, sourceChainSelector uint64, sourceChainConfig OffRampSourceChainConfig) (*types.Transaction, error) { - return _CCIPReaderTester.contract.Transact(opts, "setSourceChainConfig", sourceChainSelector, sourceChainConfig) -} - -func (_CCIPReaderTester *CCIPReaderTesterSession) SetSourceChainConfig(sourceChainSelector uint64, sourceChainConfig OffRampSourceChainConfig) (*types.Transaction, error) { - return _CCIPReaderTester.Contract.SetSourceChainConfig(&_CCIPReaderTester.TransactOpts, sourceChainSelector, sourceChainConfig) -} - -func (_CCIPReaderTester *CCIPReaderTesterTransactorSession) SetSourceChainConfig(sourceChainSelector uint64, sourceChainConfig OffRampSourceChainConfig) (*types.Transaction, error) { - return _CCIPReaderTester.Contract.SetSourceChainConfig(&_CCIPReaderTester.TransactOpts, sourceChainSelector, sourceChainConfig) -} - -type CCIPReaderTesterCCIPMessageSentIterator struct { - Event *CCIPReaderTesterCCIPMessageSent - - contract *bind.BoundContract - event string - - logs chan types.Log - sub ethereum.Subscription - done bool - fail error -} - -func (it *CCIPReaderTesterCCIPMessageSentIterator) Next() bool { - - if it.fail != nil { - return false - } - - if it.done { - select { - case log := <-it.logs: - it.Event = new(CCIPReaderTesterCCIPMessageSent) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - - select { - case log := <-it.logs: - it.Event = new(CCIPReaderTesterCCIPMessageSent) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -func (it *CCIPReaderTesterCCIPMessageSentIterator) Error() error { - return it.fail -} - -func (it *CCIPReaderTesterCCIPMessageSentIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -type CCIPReaderTesterCCIPMessageSent struct { - DestChainSelector uint64 - Message InternalEVM2AnyRampMessage - Raw types.Log -} - -func (_CCIPReaderTester *CCIPReaderTesterFilterer) FilterCCIPMessageSent(opts *bind.FilterOpts, destChainSelector []uint64) (*CCIPReaderTesterCCIPMessageSentIterator, error) { - - var destChainSelectorRule []interface{} - for _, destChainSelectorItem := range destChainSelector { - destChainSelectorRule = append(destChainSelectorRule, destChainSelectorItem) - } - - logs, sub, err := _CCIPReaderTester.contract.FilterLogs(opts, "CCIPMessageSent", destChainSelectorRule) - if err != nil { - return nil, err - } - return &CCIPReaderTesterCCIPMessageSentIterator{contract: _CCIPReaderTester.contract, event: "CCIPMessageSent", logs: logs, sub: sub}, nil -} - -func (_CCIPReaderTester *CCIPReaderTesterFilterer) WatchCCIPMessageSent(opts *bind.WatchOpts, sink chan<- *CCIPReaderTesterCCIPMessageSent, destChainSelector []uint64) (event.Subscription, error) { - - var destChainSelectorRule []interface{} - for _, destChainSelectorItem := range destChainSelector { - destChainSelectorRule = append(destChainSelectorRule, destChainSelectorItem) - } - - logs, sub, err := _CCIPReaderTester.contract.WatchLogs(opts, "CCIPMessageSent", destChainSelectorRule) - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - - event := new(CCIPReaderTesterCCIPMessageSent) - if err := _CCIPReaderTester.contract.UnpackLog(event, "CCIPMessageSent", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -func (_CCIPReaderTester *CCIPReaderTesterFilterer) ParseCCIPMessageSent(log types.Log) (*CCIPReaderTesterCCIPMessageSent, error) { - event := new(CCIPReaderTesterCCIPMessageSent) - if err := _CCIPReaderTester.contract.UnpackLog(event, "CCIPMessageSent", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -type CCIPReaderTesterCommitReportAcceptedIterator struct { - Event *CCIPReaderTesterCommitReportAccepted - - contract *bind.BoundContract - event string - - logs chan types.Log - sub ethereum.Subscription - done bool - fail error -} - -func (it *CCIPReaderTesterCommitReportAcceptedIterator) Next() bool { - - if it.fail != nil { - return false - } - - if it.done { - select { - case log := <-it.logs: - it.Event = new(CCIPReaderTesterCommitReportAccepted) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - - select { - case log := <-it.logs: - it.Event = new(CCIPReaderTesterCommitReportAccepted) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -func (it *CCIPReaderTesterCommitReportAcceptedIterator) Error() error { - return it.fail -} - -func (it *CCIPReaderTesterCommitReportAcceptedIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -type CCIPReaderTesterCommitReportAccepted struct { - MerkleRoots []InternalMerkleRoot - PriceUpdates InternalPriceUpdates - Raw types.Log -} - -func (_CCIPReaderTester *CCIPReaderTesterFilterer) FilterCommitReportAccepted(opts *bind.FilterOpts) (*CCIPReaderTesterCommitReportAcceptedIterator, error) { - - logs, sub, err := _CCIPReaderTester.contract.FilterLogs(opts, "CommitReportAccepted") - if err != nil { - return nil, err - } - return &CCIPReaderTesterCommitReportAcceptedIterator{contract: _CCIPReaderTester.contract, event: "CommitReportAccepted", logs: logs, sub: sub}, nil -} - -func (_CCIPReaderTester *CCIPReaderTesterFilterer) WatchCommitReportAccepted(opts *bind.WatchOpts, sink chan<- *CCIPReaderTesterCommitReportAccepted) (event.Subscription, error) { - - logs, sub, err := _CCIPReaderTester.contract.WatchLogs(opts, "CommitReportAccepted") - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - - event := new(CCIPReaderTesterCommitReportAccepted) - if err := _CCIPReaderTester.contract.UnpackLog(event, "CommitReportAccepted", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -func (_CCIPReaderTester *CCIPReaderTesterFilterer) ParseCommitReportAccepted(log types.Log) (*CCIPReaderTesterCommitReportAccepted, error) { - event := new(CCIPReaderTesterCommitReportAccepted) - if err := _CCIPReaderTester.contract.UnpackLog(event, "CommitReportAccepted", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -type CCIPReaderTesterExecutionStateChangedIterator struct { - Event *CCIPReaderTesterExecutionStateChanged - - contract *bind.BoundContract - event string - - logs chan types.Log - sub ethereum.Subscription - done bool - fail error -} - -func (it *CCIPReaderTesterExecutionStateChangedIterator) Next() bool { - - if it.fail != nil { - return false - } - - if it.done { - select { - case log := <-it.logs: - it.Event = new(CCIPReaderTesterExecutionStateChanged) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - - select { - case log := <-it.logs: - it.Event = new(CCIPReaderTesterExecutionStateChanged) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -func (it *CCIPReaderTesterExecutionStateChangedIterator) Error() error { - return it.fail -} - -func (it *CCIPReaderTesterExecutionStateChangedIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -type CCIPReaderTesterExecutionStateChanged struct { - SourceChainSelector uint64 - SequenceNumber uint64 - MessageId [32]byte - State uint8 - ReturnData []byte - Raw types.Log -} - -func (_CCIPReaderTester *CCIPReaderTesterFilterer) FilterExecutionStateChanged(opts *bind.FilterOpts, sourceChainSelector []uint64, sequenceNumber []uint64, messageId [][32]byte) (*CCIPReaderTesterExecutionStateChangedIterator, error) { - - var sourceChainSelectorRule []interface{} - for _, sourceChainSelectorItem := range sourceChainSelector { - sourceChainSelectorRule = append(sourceChainSelectorRule, sourceChainSelectorItem) - } - var sequenceNumberRule []interface{} - for _, sequenceNumberItem := range sequenceNumber { - sequenceNumberRule = append(sequenceNumberRule, sequenceNumberItem) - } - var messageIdRule []interface{} - for _, messageIdItem := range messageId { - messageIdRule = append(messageIdRule, messageIdItem) - } - - logs, sub, err := _CCIPReaderTester.contract.FilterLogs(opts, "ExecutionStateChanged", sourceChainSelectorRule, sequenceNumberRule, messageIdRule) - if err != nil { - return nil, err - } - return &CCIPReaderTesterExecutionStateChangedIterator{contract: _CCIPReaderTester.contract, event: "ExecutionStateChanged", logs: logs, sub: sub}, nil -} - -func (_CCIPReaderTester *CCIPReaderTesterFilterer) WatchExecutionStateChanged(opts *bind.WatchOpts, sink chan<- *CCIPReaderTesterExecutionStateChanged, sourceChainSelector []uint64, sequenceNumber []uint64, messageId [][32]byte) (event.Subscription, error) { - - var sourceChainSelectorRule []interface{} - for _, sourceChainSelectorItem := range sourceChainSelector { - sourceChainSelectorRule = append(sourceChainSelectorRule, sourceChainSelectorItem) - } - var sequenceNumberRule []interface{} - for _, sequenceNumberItem := range sequenceNumber { - sequenceNumberRule = append(sequenceNumberRule, sequenceNumberItem) - } - var messageIdRule []interface{} - for _, messageIdItem := range messageId { - messageIdRule = append(messageIdRule, messageIdItem) - } - - logs, sub, err := _CCIPReaderTester.contract.WatchLogs(opts, "ExecutionStateChanged", sourceChainSelectorRule, sequenceNumberRule, messageIdRule) - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - - event := new(CCIPReaderTesterExecutionStateChanged) - if err := _CCIPReaderTester.contract.UnpackLog(event, "ExecutionStateChanged", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -func (_CCIPReaderTester *CCIPReaderTesterFilterer) ParseExecutionStateChanged(log types.Log) (*CCIPReaderTesterExecutionStateChanged, error) { - event := new(CCIPReaderTesterExecutionStateChanged) - if err := _CCIPReaderTester.contract.UnpackLog(event, "ExecutionStateChanged", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -func (_CCIPReaderTester *CCIPReaderTester) ParseLog(log types.Log) (generated.AbigenLog, error) { - switch log.Topics[0] { - case _CCIPReaderTester.abi.Events["CCIPMessageSent"].ID: - return _CCIPReaderTester.ParseCCIPMessageSent(log) - case _CCIPReaderTester.abi.Events["CommitReportAccepted"].ID: - return _CCIPReaderTester.ParseCommitReportAccepted(log) - case _CCIPReaderTester.abi.Events["ExecutionStateChanged"].ID: - return _CCIPReaderTester.ParseExecutionStateChanged(log) - - default: - return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) - } -} - -func (CCIPReaderTesterCCIPMessageSent) Topic() common.Hash { - return common.HexToHash("0x8cd775d4a25bd349439a70817fd110144d6ab229ae1b9f54a1e5777d2041bfed") -} - -func (CCIPReaderTesterCommitReportAccepted) Topic() common.Hash { - return common.HexToHash("0x35c02761bcd3ef995c6a601a1981f4ed3934dcbe5041e24e286c89f5531d17e4") -} - -func (CCIPReaderTesterExecutionStateChanged) Topic() common.Hash { - return common.HexToHash("0x8c324ce1367b83031769f6a813e3bb4c117aba2185789d66b98b791405be6df2") -} - -func (_CCIPReaderTester *CCIPReaderTester) Address() common.Address { - return _CCIPReaderTester.address -} - -type CCIPReaderTesterInterface interface { - GetExpectedNextSequenceNumber(opts *bind.CallOpts, destChainSelector uint64) (uint64, error) - - GetInboundNonce(opts *bind.CallOpts, sourceChainSelector uint64, sender []byte) (uint64, error) - - GetSourceChainConfig(opts *bind.CallOpts, sourceChainSelector uint64) (OffRampSourceChainConfig, error) - - EmitCCIPMessageSent(opts *bind.TransactOpts, destChainSelector uint64, message InternalEVM2AnyRampMessage) (*types.Transaction, error) - - EmitCommitReportAccepted(opts *bind.TransactOpts, report OffRampCommitReport) (*types.Transaction, error) - - EmitExecutionStateChanged(opts *bind.TransactOpts, sourceChainSelector uint64, sequenceNumber uint64, messageId [32]byte, state uint8, returnData []byte) (*types.Transaction, error) - - SetDestChainSeqNr(opts *bind.TransactOpts, destChainSelector uint64, sequenceNumber uint64) (*types.Transaction, error) - - SetInboundNonce(opts *bind.TransactOpts, sourceChainSelector uint64, testNonce uint64, sender []byte) (*types.Transaction, error) - - SetSourceChainConfig(opts *bind.TransactOpts, sourceChainSelector uint64, sourceChainConfig OffRampSourceChainConfig) (*types.Transaction, error) - - FilterCCIPMessageSent(opts *bind.FilterOpts, destChainSelector []uint64) (*CCIPReaderTesterCCIPMessageSentIterator, error) - - WatchCCIPMessageSent(opts *bind.WatchOpts, sink chan<- *CCIPReaderTesterCCIPMessageSent, destChainSelector []uint64) (event.Subscription, error) - - ParseCCIPMessageSent(log types.Log) (*CCIPReaderTesterCCIPMessageSent, error) - - FilterCommitReportAccepted(opts *bind.FilterOpts) (*CCIPReaderTesterCommitReportAcceptedIterator, error) - - WatchCommitReportAccepted(opts *bind.WatchOpts, sink chan<- *CCIPReaderTesterCommitReportAccepted) (event.Subscription, error) - - ParseCommitReportAccepted(log types.Log) (*CCIPReaderTesterCommitReportAccepted, error) - - FilterExecutionStateChanged(opts *bind.FilterOpts, sourceChainSelector []uint64, sequenceNumber []uint64, messageId [][32]byte) (*CCIPReaderTesterExecutionStateChangedIterator, error) - - WatchExecutionStateChanged(opts *bind.WatchOpts, sink chan<- *CCIPReaderTesterExecutionStateChanged, sourceChainSelector []uint64, sequenceNumber []uint64, messageId [][32]byte) (event.Subscription, error) - - ParseExecutionStateChanged(log types.Log) (*CCIPReaderTesterExecutionStateChanged, error) - - ParseLog(log types.Log) (generated.AbigenLog, error) - - Address() common.Address -} diff --git a/core/gethwrappers/ccip/generated/offramp/offramp.go b/core/gethwrappers/ccip/generated/offramp/offramp.go index b1561305629..319320ff8f3 100644 --- a/core/gethwrappers/ccip/generated/offramp/offramp.go +++ b/core/gethwrappers/ccip/generated/offramp/offramp.go @@ -60,7 +60,7 @@ type InternalAny2EVMTokenTransfer struct { Amount *big.Int } -type InternalExecutionReportSingleChain struct { +type InternalExecutionReport struct { SourceChainSelector uint64 Messages []InternalAny2EVMRampMessage OffchainTokenData [][][]byte @@ -154,7 +154,7 @@ type OffRampStaticConfig struct { } var OffRampMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"contractIRMNRemote\",\"name\":\"rmnRemote\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"}],\"internalType\":\"structOffRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"feeQuoter\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageInterceptor\",\"type\":\"address\"}],\"internalType\":\"structOffRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"contractIRouter\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structOffRamp.SourceChainConfigArgs[]\",\"name\":\"sourceChainConfigs\",\"type\":\"tuple[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"CanOnlySelfCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"reportOnRamp\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"configOnRamp\",\"type\":\"bytes\"}],\"name\":\"CommitOnRampMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"expected\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"actual\",\"type\":\"bytes32\"}],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EmptyReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"err\",\"type\":\"bytes\"}],\"name\":\"ExecutionError\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"ForkedChain\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"enumMultiOCR3Base.InvalidConfigErrorType\",\"name\":\"errorType\",\"type\":\"uint8\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"got\",\"type\":\"uint256\"}],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"min\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"max\",\"type\":\"uint64\"}],\"name\":\"InvalidInterval\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"newLimit\",\"type\":\"uint256\"}],\"name\":\"InvalidManualExecutionGasLimit\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"tokenIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"oldLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenGasOverride\",\"type\":\"uint256\"}],\"name\":\"InvalidManualExecutionTokenGasOverride\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"messageDestChainSelector\",\"type\":\"uint64\"}],\"name\":\"InvalidMessageDestChainSelector\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"newState\",\"type\":\"uint8\"}],\"name\":\"InvalidNewState\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"InvalidOnRampUpdate\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRoot\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LeavesCannotBeEmpty\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"ManualExecutionGasAmountCountMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ManualExecutionGasLimitMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"ManualExecutionNotYetEnabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorReason\",\"type\":\"bytes\"}],\"name\":\"MessageValidationError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NonUniqueSignatures\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"notPool\",\"type\":\"address\"}],\"name\":\"NotACompatiblePool\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OracleCannotBeZeroAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"err\",\"type\":\"bytes\"}],\"name\":\"ReceiverError\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amountReleased\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balancePre\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balancePost\",\"type\":\"uint256\"}],\"name\":\"ReleaseOrMintBalanceMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"name\":\"RootAlreadyCommitted\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"RootNotCommitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SignatureVerificationDisabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SignaturesOutOfRegistration\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"SourceChainNotEnabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"reportSourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"messageSourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"SourceChainSelectorMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"StaleCommitReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"}],\"name\":\"StaticConfigCannotBeChanged\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"TokenDataMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"err\",\"type\":\"bytes\"}],\"name\":\"TokenHandlingError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnexpectedTokenData\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"WrongMessageLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"WrongNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroChainSelectorNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"AlreadyAttempted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRampAddress\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"maxSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"indexed\":false,\"internalType\":\"structInternal.MerkleRoot[]\",\"name\":\"merkleRoots\",\"type\":\"tuple[]\"},{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sourceToken\",\"type\":\"address\"},{\"internalType\":\"uint224\",\"name\":\"usdPerToken\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.TokenPriceUpdate[]\",\"name\":\"tokenPriceUpdates\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint224\",\"name\":\"usdPerUnitGas\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.GasPriceUpdate[]\",\"name\":\"gasPriceUpdates\",\"type\":\"tuple[]\"}],\"indexed\":false,\"internalType\":\"structInternal.PriceUpdates\",\"name\":\"priceUpdates\",\"type\":\"tuple\"}],\"name\":\"CommitReportAccepted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"feeQuoter\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageInterceptor\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structOffRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"DynamicConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"messageHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"state\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"name\":\"ExecutionStateChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"RootRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"SkippedAlreadyExecutedMessage\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"SkippedReportExecution\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"contractIRouter\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"indexed\":false,\"internalType\":\"structOffRamp.SourceChainConfig\",\"name\":\"sourceConfig\",\"type\":\"tuple\"}],\"name\":\"SourceChainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"SourceChainSelectorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"contractIRMNRemote\",\"name\":\"rmnRemote\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structOffRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"}],\"name\":\"StaticConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"Transmitted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"contractIRouter\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structOffRamp.SourceChainConfigArgs[]\",\"name\":\"sourceChainConfigUpdates\",\"type\":\"tuple[]\"}],\"name\":\"applySourceChainConfigUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"destTokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structClient.Any2EVMMessage\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"ccipReceive\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"commit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"}],\"name\":\"execute\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"destTokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"destGasAmount\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.Any2EVMTokenTransfer[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.Any2EVMRampMessage\",\"name\":\"message\",\"type\":\"tuple\"},{\"internalType\":\"bytes[]\",\"name\":\"offchainTokenData\",\"type\":\"bytes[]\"},{\"internalType\":\"uint32[]\",\"name\":\"tokenGasOverrides\",\"type\":\"uint32[]\"}],\"name\":\"executeSingleMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllSourceChainConfigs\",\"outputs\":[{\"internalType\":\"uint64[]\",\"name\":\"\",\"type\":\"uint64[]\"},{\"components\":[{\"internalType\":\"contractIRouter\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structOffRamp.SourceChainConfig[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDynamicConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"feeQuoter\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageInterceptor\",\"type\":\"address\"}],\"internalType\":\"structOffRamp.DynamicConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"getExecutionState\",\"outputs\":[{\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLatestPriceSequenceNumber\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"getMerkleRoot\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"getSourceChainConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"contractIRouter\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structOffRamp.SourceChainConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStaticConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"contractIRMNRemote\",\"name\":\"rmnRemote\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"}],\"internalType\":\"structOffRamp.StaticConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"}],\"name\":\"latestConfigDetails\",\"outputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"n\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\"}],\"internalType\":\"structMultiOCR3Base.ConfigInfo\",\"name\":\"configInfo\",\"type\":\"tuple\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"}],\"internalType\":\"structMultiOCR3Base.OCRConfig\",\"name\":\"ocrConfig\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"destTokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"destGasAmount\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.Any2EVMTokenTransfer[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.Any2EVMRampMessage[]\",\"name\":\"messages\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[][]\",\"name\":\"offchainTokenData\",\"type\":\"bytes[][]\"},{\"internalType\":\"bytes32[]\",\"name\":\"proofs\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"proofFlagBits\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.ExecutionReportSingleChain[]\",\"name\":\"reports\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"receiverExecutionGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint32[]\",\"name\":\"tokenGasOverrides\",\"type\":\"uint32[]\"}],\"internalType\":\"structOffRamp.GasLimitOverride[][]\",\"name\":\"gasLimitOverrides\",\"type\":\"tuple[][]\"}],\"name\":\"manuallyExecute\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"feeQuoter\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageInterceptor\",\"type\":\"address\"}],\"internalType\":\"structOffRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"setDynamicConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"}],\"internalType\":\"structMultiOCR3Base.OCRConfigArgs[]\",\"name\":\"ocrConfigArgs\",\"type\":\"tuple[]\"}],\"name\":\"setOCR3Configs\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"contractIRMNRemote\",\"name\":\"rmnRemote\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"}],\"internalType\":\"structOffRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"feeQuoter\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageInterceptor\",\"type\":\"address\"}],\"internalType\":\"structOffRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"contractIRouter\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structOffRamp.SourceChainConfigArgs[]\",\"name\":\"sourceChainConfigs\",\"type\":\"tuple[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"CanOnlySelfCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"reportOnRamp\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"configOnRamp\",\"type\":\"bytes\"}],\"name\":\"CommitOnRampMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"expected\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"actual\",\"type\":\"bytes32\"}],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EmptyReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"err\",\"type\":\"bytes\"}],\"name\":\"ExecutionError\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"ForkedChain\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"enumMultiOCR3Base.InvalidConfigErrorType\",\"name\":\"errorType\",\"type\":\"uint8\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"got\",\"type\":\"uint256\"}],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"min\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"max\",\"type\":\"uint64\"}],\"name\":\"InvalidInterval\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"newLimit\",\"type\":\"uint256\"}],\"name\":\"InvalidManualExecutionGasLimit\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"tokenIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"oldLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenGasOverride\",\"type\":\"uint256\"}],\"name\":\"InvalidManualExecutionTokenGasOverride\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"messageDestChainSelector\",\"type\":\"uint64\"}],\"name\":\"InvalidMessageDestChainSelector\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"newState\",\"type\":\"uint8\"}],\"name\":\"InvalidNewState\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"InvalidOnRampUpdate\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRoot\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LeavesCannotBeEmpty\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"ManualExecutionGasAmountCountMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ManualExecutionGasLimitMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"ManualExecutionNotYetEnabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorReason\",\"type\":\"bytes\"}],\"name\":\"MessageValidationError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NonUniqueSignatures\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"notPool\",\"type\":\"address\"}],\"name\":\"NotACompatiblePool\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OracleCannotBeZeroAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"err\",\"type\":\"bytes\"}],\"name\":\"ReceiverError\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amountReleased\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balancePre\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balancePost\",\"type\":\"uint256\"}],\"name\":\"ReleaseOrMintBalanceMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"name\":\"RootAlreadyCommitted\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"RootNotCommitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SignatureVerificationDisabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SignaturesOutOfRegistration\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"SourceChainNotEnabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"reportSourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"messageSourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"SourceChainSelectorMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"StaleCommitReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"}],\"name\":\"StaticConfigCannotBeChanged\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"TokenDataMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"err\",\"type\":\"bytes\"}],\"name\":\"TokenHandlingError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnexpectedTokenData\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"WrongMessageLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"WrongNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroChainSelectorNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"AlreadyAttempted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRampAddress\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"maxSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"indexed\":false,\"internalType\":\"structInternal.MerkleRoot[]\",\"name\":\"merkleRoots\",\"type\":\"tuple[]\"},{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sourceToken\",\"type\":\"address\"},{\"internalType\":\"uint224\",\"name\":\"usdPerToken\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.TokenPriceUpdate[]\",\"name\":\"tokenPriceUpdates\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint224\",\"name\":\"usdPerUnitGas\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.GasPriceUpdate[]\",\"name\":\"gasPriceUpdates\",\"type\":\"tuple[]\"}],\"indexed\":false,\"internalType\":\"structInternal.PriceUpdates\",\"name\":\"priceUpdates\",\"type\":\"tuple\"}],\"name\":\"CommitReportAccepted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"feeQuoter\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageInterceptor\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structOffRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"DynamicConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"messageHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"state\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"name\":\"ExecutionStateChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"RootRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"SkippedAlreadyExecutedMessage\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"SkippedReportExecution\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"contractIRouter\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"indexed\":false,\"internalType\":\"structOffRamp.SourceChainConfig\",\"name\":\"sourceConfig\",\"type\":\"tuple\"}],\"name\":\"SourceChainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"SourceChainSelectorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"contractIRMNRemote\",\"name\":\"rmnRemote\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structOffRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"}],\"name\":\"StaticConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"Transmitted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"contractIRouter\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structOffRamp.SourceChainConfigArgs[]\",\"name\":\"sourceChainConfigUpdates\",\"type\":\"tuple[]\"}],\"name\":\"applySourceChainConfigUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"destTokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structClient.Any2EVMMessage\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"ccipReceive\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"commit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"}],\"name\":\"execute\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"destTokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"destGasAmount\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.Any2EVMTokenTransfer[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.Any2EVMRampMessage\",\"name\":\"message\",\"type\":\"tuple\"},{\"internalType\":\"bytes[]\",\"name\":\"offchainTokenData\",\"type\":\"bytes[]\"},{\"internalType\":\"uint32[]\",\"name\":\"tokenGasOverrides\",\"type\":\"uint32[]\"}],\"name\":\"executeSingleMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllSourceChainConfigs\",\"outputs\":[{\"internalType\":\"uint64[]\",\"name\":\"\",\"type\":\"uint64[]\"},{\"components\":[{\"internalType\":\"contractIRouter\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structOffRamp.SourceChainConfig[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDynamicConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"feeQuoter\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageInterceptor\",\"type\":\"address\"}],\"internalType\":\"structOffRamp.DynamicConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"getExecutionState\",\"outputs\":[{\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLatestPriceSequenceNumber\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"getMerkleRoot\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"getSourceChainConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"contractIRouter\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structOffRamp.SourceChainConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStaticConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"contractIRMNRemote\",\"name\":\"rmnRemote\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"}],\"internalType\":\"structOffRamp.StaticConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"}],\"name\":\"latestConfigDetails\",\"outputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"n\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\"}],\"internalType\":\"structMultiOCR3Base.ConfigInfo\",\"name\":\"configInfo\",\"type\":\"tuple\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"}],\"internalType\":\"structMultiOCR3Base.OCRConfig\",\"name\":\"ocrConfig\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"destTokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"destGasAmount\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.Any2EVMTokenTransfer[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.Any2EVMRampMessage[]\",\"name\":\"messages\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[][]\",\"name\":\"offchainTokenData\",\"type\":\"bytes[][]\"},{\"internalType\":\"bytes32[]\",\"name\":\"proofs\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"proofFlagBits\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.ExecutionReport[]\",\"name\":\"reports\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"receiverExecutionGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint32[]\",\"name\":\"tokenGasOverrides\",\"type\":\"uint32[]\"}],\"internalType\":\"structOffRamp.GasLimitOverride[][]\",\"name\":\"gasLimitOverrides\",\"type\":\"tuple[][]\"}],\"name\":\"manuallyExecute\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"feeQuoter\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageInterceptor\",\"type\":\"address\"}],\"internalType\":\"structOffRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"setDynamicConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"}],\"internalType\":\"structMultiOCR3Base.OCRConfigArgs[]\",\"name\":\"ocrConfigArgs\",\"type\":\"tuple[]\"}],\"name\":\"setOCR3Configs\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", Bin: "0x6101206040523480156200001257600080fd5b5060405162006c4638038062006c46833981016040819052620000359162000885565b33806000816200008c5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000bf57620000bf81620001fa565b5050466080525060208301516001600160a01b03161580620000ec575060408301516001600160a01b0316155b8062000103575060608301516001600160a01b0316155b1562000122576040516342bcdf7f60e11b815260040160405180910390fd5b82516001600160401b03166000036200014e5760405163c656089560e01b815260040160405180910390fd5b82516001600160401b0390811660a052602080850180516001600160a01b0390811660c05260408088018051831660e0526060808a01805185166101005283518b519098168852945184169587019590955251821690850152905116908201527f683eb52ee924eb817377cfa8f41f238f4bb7a877da5267869dfffbad85f564d89060800160405180910390a1620001e682620002a5565b620001f1816200036d565b50505062000c0c565b336001600160a01b03821603620002545760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000083565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b80516001600160a01b0316620002ce576040516342bcdf7f60e11b815260040160405180910390fd5b805160048054602080850180516001600160a01b039586166001600160c01b03199094168417600160a01b63ffffffff928316021790945560408087018051600580546001600160a01b031916918916919091179055815194855291519094169183019190915251909216908201527fa1c15688cb2c24508e158f6942b9276c6f3028a85e1af8cf3fff0c3ff3d5fc8d9060600160405180910390a150565b60005b8151811015620005d0576000828281518110620003915762000391620009c2565b60200260200101519050600081602001519050806001600160401b0316600003620003cf5760405163c656089560e01b815260040160405180910390fd5b81516001600160a01b0316620003f8576040516342bcdf7f60e11b815260040160405180910390fd5b6001600160401b038116600090815260086020526040902060608301516001820180546200042690620009d8565b905060000362000489578154600160a81b600160e81b031916600160a81b1782556040516001600160401b03841681527ff4c1390c70e5c0f491ae1ccbc06f9117cbbadf2767b247b3bc203280f24c0fb99060200160405180910390a1620004c8565b8154600160a81b90046001600160401b0316600114620004c857604051632105803760e11b81526001600160401b038416600482015260240162000083565b80511580620004fe5750604080516000602082015201604051602081830303815290604052805190602001208180519060200120145b156200051d576040516342bcdf7f60e11b815260040160405180910390fd5b600182016200052d828262000a69565b506040840151825485516001600160a01b03166001600160a01b0319921515600160a01b02929092166001600160a81b0319909116171782556200057c60066001600160401b038516620005d4565b50826001600160401b03167f49f51971edd25182e97182d6ea372a0488ce2ab639f6a3a7ab4df0d2636fe56b83604051620005b8919062000b35565b60405180910390a25050505080600101905062000370565b5050565b6000620005e28383620005eb565b90505b92915050565b60008181526001830160205260408120546200063457508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155620005e5565b506000620005e5565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b03811182821017156200067857620006786200063d565b60405290565b604051601f8201601f191681016001600160401b0381118282101715620006a957620006a96200063d565b604052919050565b80516001600160401b0381168114620006c957600080fd5b919050565b6001600160a01b0381168114620006e457600080fd5b50565b6000601f83601f840112620006fb57600080fd5b825160206001600160401b03808311156200071a576200071a6200063d565b8260051b6200072b8382016200067e565b93845286810183019383810190898611156200074657600080fd5b84890192505b858310156200087857825184811115620007665760008081fd5b89016080601f19828d038101821315620007805760008081fd5b6200078a62000653565b888401516200079981620006ce565b81526040620007aa858201620006b1565b8a8301526060808601518015158114620007c45760008081fd5b83830152938501519389851115620007dc5760008081fd5b84860195508f603f870112620007f457600094508485fd5b8a8601519450898511156200080d576200080d6200063d565b6200081e8b858f880116016200067e565b93508484528f82868801011115620008365760008081fd5b60005b8581101562000856578681018301518582018d01528b0162000839565b5060009484018b0194909452509182015283525091840191908401906200074c565b9998505050505050505050565b60008060008385036101008112156200089d57600080fd5b6080811215620008ac57600080fd5b620008b662000653565b620008c186620006b1565b81526020860151620008d381620006ce565b60208201526040860151620008e881620006ce565b60408201526060860151620008fd81620006ce565b606082810191909152909450607f19820112156200091a57600080fd5b50604051606081016001600160401b0380821183831017156200094157620009416200063d565b81604052608087015191506200095782620006ce565b90825260a08601519063ffffffff821682146200097357600080fd5b81602084015260c087015191506200098b82620006ce565b6040830182905260e087015192945080831115620009a857600080fd5b5050620009b886828701620006e7565b9150509250925092565b634e487b7160e01b600052603260045260246000fd5b600181811c90821680620009ed57607f821691505b60208210810362000a0e57634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111562000a64576000816000526020600020601f850160051c8101602086101562000a3f5750805b601f850160051c820191505b8181101562000a605782815560010162000a4b565b5050505b505050565b81516001600160401b0381111562000a855762000a856200063d565b62000a9d8162000a968454620009d8565b8462000a14565b602080601f83116001811462000ad5576000841562000abc5750858301515b600019600386901b1c1916600185901b17855562000a60565b600085815260208120601f198616915b8281101562000b065788860151825594840194600190910190840162000ae5565b508582101562000b255787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b602080825282546001600160a01b0381168383015260a081901c60ff161515604084015260a81c6001600160401b0316606083015260808083015260018084018054600093929190849062000b8a81620009d8565b8060a089015260c0600183166000811462000bae576001811462000bcb5762000bfd565b60ff19841660c08b015260c083151560051b8b0101945062000bfd565b85600052602060002060005b8481101562000bf45781548c820185015290880190890162000bd7565b8b0160c0019550505b50929998505050505050505050565b60805160a05160c05160e05161010051615fc462000c82600039600081816102470152612c970152600081816102180152612f860152600081816101e9015281816105890152818161073b01526126580152600081816101ba0152612897015260008181611d7d0152611db00152615fc46000f3fe608060405234801561001057600080fd5b506004361061016c5760003560e01c80636f9e320f116100cd578063c673e58411610081578063e9d68a8e11610066578063e9d68a8e146104ed578063f2fde38b1461050d578063f716f99f1461052057600080fd5b8063c673e58414610489578063ccd37ba3146104a957600080fd5b806379ba5097116100b257806379ba50971461045857806385572ffb146104605780638da5cb5b1461046e57600080fd5b80636f9e320f146103b35780637437ff9f146103c657600080fd5b80633f4b04aa116101245780635e36480c116101095780635e36480c1461036d5780635e7bb0081461038d57806360987c20146103a057600080fd5b80633f4b04aa1461033c5780635215505b1461035757600080fd5b8063181f5a7711610155578063181f5a77146102cd5780632d04ab7614610316578063311cd5131461032957600080fd5b806304666f9c1461017157806306285c6914610186575b600080fd5b61018461017f366004613edb565b610533565b005b61027760408051608081018252600080825260208201819052918101829052606081019190915260405180608001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160401b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316815250905090565b6040516102c4919081516001600160401b031681526020808301516001600160a01b0390811691830191909152604080840151821690830152606092830151169181019190915260800190565b60405180910390f35b6103096040518060400160405280601181526020017f4f666652616d7020312e362e302d64657600000000000000000000000000000081525081565b6040516102c49190614049565b6101846103243660046140f9565b610547565b6101846103373660046141ab565b610a4e565b600b546040516001600160401b0390911681526020016102c4565b61035f610ab7565b6040516102c4929190614245565b61038061037b3660046142e6565b610d12565b6040516102c49190614343565b61018461039b3660046148ac565b610d67565b6101846103ae366004614af0565b610ff6565b6101846103c1366004614b84565b6112d6565b610422604080516060810182526000808252602082018190529181019190915250604080516060810182526004546001600160a01b038082168352600160a01b90910463ffffffff166020830152600554169181019190915290565b6040805182516001600160a01b03908116825260208085015163ffffffff169083015292820151909216908201526060016102c4565b6101846112e7565b61018461016c366004614bf3565b6000546040516001600160a01b0390911681526020016102c4565b61049c610497366004614c3e565b611398565b6040516102c49190614c9e565b6104df6104b7366004614d13565b6001600160401b03919091166000908152600a60209081526040808320938352929052205490565b6040519081526020016102c4565b6105006104fb366004614d3d565b6114f6565b6040516102c49190614d58565b61018461051b366004614d6b565b611602565b61018461052e366004614df0565b611613565b61053b611655565b610544816116b1565b50565b600061055587890189615145565b602081015151909150156105f257602081015160408083015160608401519151638d8741cb60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001693638d8741cb936105c193309390919060040161537a565b60006040518083038186803b1580156105d957600080fd5b505afa1580156105ed573d6000803e3d6000fd5b505050505b8051515115158061060857508051602001515115155b156106d457600b5460208a0135906001600160401b03808316911610156106ac57600b805467ffffffffffffffff19166001600160401b038316179055600480548351604051633937306f60e01b81526001600160a01b0390921692633937306f926106759291016154c7565b600060405180830381600087803b15801561068f57600080fd5b505af11580156106a3573d6000803e3d6000fd5b505050506106d2565b8160200151516000036106d257604051632261116760e01b815260040160405180910390fd5b505b60005b81602001515181101561098f576000826020015182815181106106fc576106fc6153f5565b60209081029190910101518051604051632cbc26bb60e01b815267ffffffffffffffff60801b608083901b166004820152919250906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632cbc26bb90602401602060405180830381865afa158015610782573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a691906154da565b156107d457604051637edeb53960e11b81526001600160401b03821660048201526024015b60405180910390fd5b60006107df82611918565b9050806001016040516107f29190615531565b60405180910390208360200151805190602001201461082f5782602001518160010160405163b80d8fa960e01b81526004016107cb929190615624565b60408301518154600160a81b90046001600160401b039081169116141580610870575082606001516001600160401b031683604001516001600160401b0316115b156108b557825160408085015160608601519151636af0786b60e11b81526001600160401b0393841660048201529083166024820152911660448201526064016107cb565b6080830151806108d85760405163504570e360e01b815260040160405180910390fd5b83516001600160401b03166000908152600a60209081526040808320848452909152902054156109305783516040516332cf0cbf60e01b81526001600160401b039091166004820152602481018290526044016107cb565b606084015161094090600161565f565b825467ffffffffffffffff60a81b1916600160a81b6001600160401b0392831602179092559251166000908152600a6020908152604080832094835293905291909120429055506001016106d7565b50602081015181516040517f35c02761bcd3ef995c6a601a1981f4ed3934dcbe5041e24e286c89f5531d17e4926109c7929091615686565b60405180910390a1610a4360008a8a8a8a8a8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808e0282810182019093528d82529093508d92508c9182918501908490808284376000920191909152508b9250611964915050565b505050505050505050565b610a8e610a5d828401846156ab565b6040805160008082526020820190925290610a88565b6060815260200190600190039081610a735790505b50611c5d565b604080516000808252602082019092529050610ab1600185858585866000611964565b50505050565b6060806000610ac66006611d1f565b6001600160401b03811115610add57610add613d1d565b604051908082528060200260200182016040528015610b2e57816020015b6040805160808101825260008082526020808301829052928201526060808201528252600019909201910181610afb5790505b5090506000610b3d6006611d1f565b6001600160401b03811115610b5457610b54613d1d565b604051908082528060200260200182016040528015610b7d578160200160208202803683370190505b50905060005b610b8d6006611d1f565b811015610d0957610b9f600682611d29565b828281518110610bb157610bb16153f5565b60200260200101906001600160401b031690816001600160401b03168152505060086000838381518110610be757610be76153f5565b6020908102919091018101516001600160401b039081168352828201939093526040918201600020825160808101845281546001600160a01b038116825260ff600160a01b820416151593820193909352600160a81b90920490931691810191909152600182018054919291606084019190610c62906154f7565b80601f0160208091040260200160405190810160405280929190818152602001828054610c8e906154f7565b8015610cdb5780601f10610cb057610100808354040283529160200191610cdb565b820191906000526020600020905b815481529060010190602001808311610cbe57829003601f168201915b505050505081525050838281518110610cf657610cf66153f5565b6020908102919091010152600101610b83565b50939092509050565b6000610d20600160046156df565b6002610d2d608085615708565b6001600160401b0316610d40919061572e565b610d4a8585611d35565b901c166003811115610d5e57610d5e614319565b90505b92915050565b610d6f611d7a565b815181518114610d92576040516320f8fd5960e21b815260040160405180910390fd5b60005b81811015610fe6576000848281518110610db157610db16153f5565b60200260200101519050600081602001515190506000858481518110610dd957610dd96153f5565b6020026020010151905080518214610e04576040516320f8fd5960e21b815260040160405180910390fd5b60005b82811015610fd7576000828281518110610e2357610e236153f5565b6020026020010151600001519050600085602001518381518110610e4957610e496153f5565b6020026020010151905081600014610e9d578060800151821015610e9d578551815151604051633a98d46360e11b81526001600160401b0390921660048301526024820152604481018390526064016107cb565b838381518110610eaf57610eaf6153f5565b602002602001015160200151518160a001515114610efc57805180516060909101516040516370a193fd60e01b815260048101929092526001600160401b031660248201526044016107cb565b60005b8160a0015151811015610fc9576000858581518110610f2057610f206153f5565b6020026020010151602001518281518110610f3d57610f3d6153f5565b602002602001015163ffffffff16905080600014610fc05760008360a001518381518110610f6d57610f6d6153f5565b60200260200101516040015163ffffffff16905080821015610fbe578351516040516348e617b360e01b815260048101919091526024810184905260448101829052606481018390526084016107cb565b505b50600101610eff565b505050806001019050610e07565b50505050806001019050610d95565b50610ff18383611c5d565b505050565b333014611016576040516306e34e6560e31b815260040160405180910390fd5b6040805160008082526020820190925281611053565b604080518082019091526000808252602082015281526020019060019003908161102c5790505b5060a08701515190915015611089576110868660a001518760200151886060015189600001516020015189898989611de2565b90505b6040805160a081018252875151815287516020908101516001600160401b0316818301528089015183516000948401926110c4929101614049565b60408051601f19818403018152918152908252898101516020830152018390526005549091506001600160a01b0316801561119f576040516308d450a160e01b81526001600160a01b038216906308d450a1906111259085906004016157e6565b600060405180830381600087803b15801561113f57600080fd5b505af1925050508015611150575060015b61119f573d80801561117e576040519150601f19603f3d011682016040523d82523d6000602084013e611183565b606091505b50806040516309c2532560e01b81526004016107cb9190614049565b6040880151511580156111b457506080880151155b806111cb575060608801516001600160a01b03163b155b806111f2575060608801516111f0906001600160a01b03166385572ffb60e01b611f93565b155b156111ff575050506112cf565b87516020908101516001600160401b03166000908152600890915260408082205460808b015160608c01519251633cf9798360e01b815284936001600160a01b0390931692633cf979839261125d92899261138892916004016157f9565b6000604051808303816000875af115801561127c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112a49190810190615835565b5091509150816112c957806040516302a35ba360e21b81526004016107cb9190614049565b50505050505b5050505050565b6112de611655565b61054481611faf565b6001546001600160a01b031633146113415760405162461bcd60e51b815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016107cb565b60008054336001600160a01b0319808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6113db6040805160e081019091526000606082018181526080830182905260a0830182905260c08301919091528190815260200160608152602001606081525090565b60ff808316600090815260026020818152604092839020835160e081018552815460608201908152600183015480881660808401526101008104881660a0840152620100009004909616151560c08201529485529182018054845181840281018401909552808552929385830193909283018282801561148457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611466575b50505050508152602001600382018054806020026020016040519081016040528092919081815260200182805480156114e657602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116114c8575b5050505050815250509050919050565b60408051608080820183526000808352602080840182905283850182905260608085018190526001600160401b03878116845260088352928690208651948501875280546001600160a01b0381168652600160a01b810460ff16151593860193909352600160a81b909204909216948301949094526001840180549394929391840191611582906154f7565b80601f01602080910402602001604051908101604052809291908181526020018280546115ae906154f7565b80156114e65780601f106115d0576101008083540402835291602001916114e6565b820191906000526020600020905b8154815290600101906020018083116115de57505050919092525091949350505050565b61160a611655565b6105448161208e565b61161b611655565b60005b81518110156116515761164982828151811061163c5761163c6153f5565b6020026020010151612137565b60010161161e565b5050565b6000546001600160a01b031633146116af5760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016107cb565b565b60005b81518110156116515760008282815181106116d1576116d16153f5565b60200260200101519050600081602001519050806001600160401b031660000361170e5760405163c656089560e01b815260040160405180910390fd5b81516001600160a01b0316611736576040516342bcdf7f60e11b815260040160405180910390fd5b6001600160401b03811660009081526008602052604090206060830151600182018054611762906154f7565b90506000036117c457815467ffffffffffffffff60a81b1916600160a81b1782556040516001600160401b03841681527ff4c1390c70e5c0f491ae1ccbc06f9117cbbadf2767b247b3bc203280f24c0fb99060200160405180910390a1611801565b8154600160a81b90046001600160401b031660011461180157604051632105803760e11b81526001600160401b03841660048201526024016107cb565b805115806118365750604080516000602082015201604051602081830303815290604052805190602001208180519060200120145b15611854576040516342bcdf7f60e11b815260040160405180910390fd5b60018201611862828261591a565b506040840151825485516001600160a01b03166001600160a01b0319921515600160a01b02929092167fffffffffffffffffffffff000000000000000000000000000000000000000000909116171782556118c760066001600160401b038516612461565b50826001600160401b03167f49f51971edd25182e97182d6ea372a0488ce2ab639f6a3a7ab4df0d2636fe56b8360405161190191906159d9565b60405180910390a2505050508060010190506116b4565b6001600160401b03811660009081526008602052604081208054600160a01b900460ff16610d615760405163ed053c5960e01b81526001600160401b03841660048201526024016107cb565b60ff878116600090815260026020908152604080832081516080810183528154815260019091015480861693820193909352610100830485169181019190915262010000909104909216151560608301528735906119c38760a4615a27565b9050826060015115611a0b5784516119dc90602061572e565b86516119e990602061572e565b6119f49060a0615a27565b6119fe9190615a27565b611a089082615a27565b90505b368114611a3457604051638e1192e160e01b8152600481018290523660248201526044016107cb565b5081518114611a635781516040516324f7d61360e21b81526004810191909152602481018290526044016107cb565b611a6b611d7a565b60ff808a1660009081526003602090815260408083203384528252808320815180830190925280548086168352939491939092840191610100909104166002811115611ab957611ab9614319565b6002811115611aca57611aca614319565b9052509050600281602001516002811115611ae757611ae7614319565b148015611b3b5750600260008b60ff1660ff168152602001908152602001600020600301816000015160ff1681548110611b2357611b236153f5565b6000918252602090912001546001600160a01b031633145b611b5857604051631b41e11d60e31b815260040160405180910390fd5b50816060015115611c08576020820151611b73906001615a3a565b60ff16855114611b96576040516371253a2560e01b815260040160405180910390fd5b8351855114611bb85760405163a75d88af60e01b815260040160405180910390fd5b60008787604051611bca929190615a53565b604051908190038120611be1918b90602001615a63565b604051602081830303815290604052805190602001209050611c068a8288888861246d565b505b6040805182815260208a8101356001600160401b03169082015260ff8b16917f198d6990ef96613a9026203077e422916918b03ff47f0be6bee7b02d8e139ef0910160405180910390a2505050505050505050565b8151600003611c7e5760405162bf199760e01b815260040160405180910390fd5b80516040805160008082526020820190925291159181611cc1565b604080518082019091526000815260606020820152815260200190600190039081611c995790505b50905060005b84518110156112cf57611d17858281518110611ce557611ce56153f5565b602002602001015184611d1157858381518110611d0457611d046153f5565b602002602001015161262a565b8361262a565b600101611cc7565b6000610d61825490565b6000610d5e8383612f21565b6001600160401b038216600090815260096020526040812081611d59608085615a77565b6001600160401b031681526020810191909152604001600020549392505050565b467f0000000000000000000000000000000000000000000000000000000000000000146116af57604051630f01ce8560e01b81527f000000000000000000000000000000000000000000000000000000000000000060048201524660248201526044016107cb565b606088516001600160401b03811115611dfd57611dfd613d1d565b604051908082528060200260200182016040528015611e4257816020015b6040805180820190915260008082526020820152815260200190600190039081611e1b5790505b509050811560005b8a51811015611f855781611ee257848482818110611e6a57611e6a6153f5565b9050602002016020810190611e7f9190615a9d565b63ffffffff1615611ee257848482818110611e9c57611e9c6153f5565b9050602002016020810190611eb19190615a9d565b8b8281518110611ec357611ec36153f5565b60200260200101516040019063ffffffff16908163ffffffff16815250505b611f608b8281518110611ef757611ef76153f5565b60200260200101518b8b8b8b8b87818110611f1457611f146153f5565b9050602002810190611f269190615ab8565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612f4b92505050565b838281518110611f7257611f726153f5565b6020908102919091010152600101611e4a565b505098975050505050505050565b6000611f9e8361322b565b8015610d5e5750610d5e8383613276565b80516001600160a01b0316611fd7576040516342bcdf7f60e11b815260040160405180910390fd5b805160048054602080850180516001600160a01b039586167fffffffffffffffff0000000000000000000000000000000000000000000000009094168417600160a01b63ffffffff928316021790945560408087018051600580546001600160a01b031916918916919091179055815194855291519094169183019190915251909216908201527fa1c15688cb2c24508e158f6942b9276c6f3028a85e1af8cf3fff0c3ff3d5fc8d9060600160405180910390a150565b336001600160a01b038216036120e65760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016107cb565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b806040015160ff16600003612162576000604051631b3fab5160e11b81526004016107cb9190615afe565b60208082015160ff808216600090815260029093526040832060018101549293909283921690036121b3576060840151600182018054911515620100000262ff0000199092169190911790556121ef565b6060840151600182015460ff62010000909104161515901515146121ef576040516321fd80df60e21b815260ff841660048201526024016107cb565b60a08401518051610100101561221b576001604051631b3fab5160e11b81526004016107cb9190615afe565b8051600003612240576005604051631b3fab5160e11b81526004016107cb9190615afe565b6122a6848460030180548060200260200160405190810160405280929190818152602001828054801561229c57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161227e575b5050505050613318565b8460600151156123d657612314848460020180548060200260200160405190810160405280929190818152602001828054801561229c576020028201919060005260206000209081546001600160a01b0316815260019091019060200180831161227e575050505050613318565b608085015180516101001015612340576002604051631b3fab5160e11b81526004016107cb9190615afe565b6040860151612350906003615b18565b60ff16815111612376576003604051631b3fab5160e11b81526004016107cb9190615afe565b81518151101561239c576001604051631b3fab5160e11b81526004016107cb9190615afe565b805160018401805461ff00191661010060ff8416021790556123c79060028601906020840190613ca3565b506123d485826001613381565b505b6123e284826002613381565b80516123f79060038501906020840190613ca3565b5060408581015160018401805460ff191660ff8316179055865180855560a088015192517fab8b1b57514019638d7b5ce9c638fe71366fe8e2be1c40a7a80f1733d0e9f547936124509389939260028a01929190615b34565b60405180910390a16112cf846134dc565b6000610d5e8383613533565b8251600090815b81811015612620576000600188868460208110612493576124936153f5565b6124a091901a601b615a3a565b8985815181106124b2576124b26153f5565b60200260200101518986815181106124cc576124cc6153f5565b60200260200101516040516000815260200160405260405161250a949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa15801561252c573d6000803e3d6000fd5b505060408051601f1981015160ff808e166000908152600360209081528582206001600160a01b0385168352815285822085870190965285548084168652939750909550929392840191610100900416600281111561258d5761258d614319565b600281111561259e5761259e614319565b90525090506001816020015160028111156125bb576125bb614319565b146125d957604051636518c33d60e11b815260040160405180910390fd5b8051600160ff9091161b85161561260357604051633d9ef1f160e21b815260040160405180910390fd5b806000015160ff166001901b851794505050806001019050612474565b5050505050505050565b81518151604051632cbc26bb60e01b8152608083901b67ffffffffffffffff60801b166004820152901515907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632cbc26bb90602401602060405180830381865afa1580156126a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126cb91906154da565b1561273c5780156126fa57604051637edeb53960e11b81526001600160401b03831660048201526024016107cb565b6040516001600160401b03831681527faab522ed53d887e56ed53dd37398a01aeef6a58e0fa77c2173beb9512d8949339060200160405180910390a150505050565b600061274783611918565b6001018054612755906154f7565b80601f0160208091040260200160405190810160405280929190818152602001828054612781906154f7565b80156127ce5780601f106127a3576101008083540402835291602001916127ce565b820191906000526020600020905b8154815290600101906020018083116127b157829003601f168201915b505050602088015151929350505060008190036127fd5760405162bf199760e01b815260040160405180910390fd5b8560400151518114612822576040516357e0e08360e01b815260040160405180910390fd5b6000816001600160401b0381111561283c5761283c613d1d565b604051908082528060200260200182016040528015612865578160200160208202803683370190505b50905060005b82811015612a095760008860200151828151811061288b5761288b6153f5565b602002602001015190507f00000000000000000000000000000000000000000000000000000000000000006001600160401b03168160000151604001516001600160401b0316146129025780516040908101519051631c21951160e11b81526001600160401b0390911660048201526024016107cb565b866001600160401b03168160000151602001516001600160401b03161461295657805160200151604051636c95f1eb60e01b81526001600160401b03808a16600483015290911660248201526044016107cb565b6129e3817f2425b0b9f9054c76ff151b0a175b18f37a4a4e82013a72e9f15c9caa095ed21f83600001516020015184600001516040015189805190602001206040516020016129c894939291909384526001600160401b03928316602085015291166040830152606082015260800190565b60405160208183030381529060405280519060200120613582565b8383815181106129f5576129f56153f5565b60209081029190910101525060010161286b565b506000612a2086838a606001518b6080015161368a565b905080600003612a4e57604051633ee8bd3f60e11b81526001600160401b03871660048201526024016107cb565b60005b83811015610a435760005a905060008a602001518381518110612a7657612a766153f5565b602002602001015190506000612a948a836000015160600151610d12565b90506000816003811115612aaa57612aaa614319565b1480612ac757506003816003811115612ac557612ac5614319565b145b612b1d57815160600151604080516001600160401b03808e16825290921660208301527f3b575419319662b2a6f5e2467d84521517a3382b908eb3d557bb3fdb0c50e23c910160405180910390a1505050612f19565b60608915612bfc578b8581518110612b3757612b376153f5565b6020908102919091018101510151600454909150600090600160a01b900463ffffffff16612b6588426156df565b1190508080612b8557506003836003811115612b8357612b83614319565b145b612bad576040516354e7e43160e11b81526001600160401b038d1660048201526024016107cb565b8c8681518110612bbf57612bbf6153f5565b602002602001015160000151600014612bf6578c8681518110612be457612be46153f5565b60209081029190910101515160808501525b50612c68565b6000826003811115612c1057612c10614319565b14612c6857825160600151604080516001600160401b03808f16825290921660208301527f3ef2a99c550a751d4b0b261268f05a803dfb049ab43616a1ffb388f61fe65120910160405180910390a150505050612f19565b8251608001516001600160401b031615612d41576000826003811115612c9057612c90614319565b03612d41577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663e0e03cae8c85600001516080015186602001516040518463ffffffff1660e01b8152600401612cf193929190615be6565b6020604051808303816000875af1158015612d10573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d3491906154da565b612d415750505050612f19565b60008d604001518681518110612d5957612d596153f5565b6020026020010151905080518460a001515114612da357835160600151604051631cfe6d8b60e01b81526001600160401b03808f16600483015290911660248201526044016107cb565b612db78c85600001516060015160016136c7565b600080612dc586848661376c565b91509150612ddc8e876000015160600151846136c7565b8c15612e33576003826003811115612df657612df6614319565b03612e33576000856003811115612e0f57612e0f614319565b14612e3357855151604051632b11b8d960e01b81526107cb91908390600401615c12565b6002826003811115612e4757612e47614319565b14612e8c576003826003811115612e6057612e60614319565b14612e8c578d866000015160600151836040516349362d1f60e11b81526004016107cb93929190615c2b565b8560000151600001518660000151606001516001600160401b03168f6001600160401b03167f05665fe9ad095383d018353f4cbcba77e84db27dd215081bbf7cdf9ae6fbe48b8d8c81518110612ee457612ee46153f5565b602002602001015186865a612ef9908f6156df565b604051612f099493929190615c50565b60405180910390a4505050505050505b600101612a51565b6000826000018281548110612f3857612f386153f5565b9060005260206000200154905092915050565b6040805180820190915260008082526020820152602086015160405163bbe4f6db60e01b81526001600160a01b0380831660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063bbe4f6db90602401602060405180830381865afa158015612fcf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ff39190615c87565b90506001600160a01b038116158061302257506130206001600160a01b03821663aff2afbf60e01b611f93565b155b1561304b5760405163ae9b4ce960e01b81526001600160a01b03821660048201526024016107cb565b60008061306388858c6040015163ffffffff16613820565b9150915060008060006131166040518061010001604052808e81526020018c6001600160401b031681526020018d6001600160a01b031681526020018f608001518152602001896001600160a01b031681526020018f6000015181526020018f6060015181526020018b8152506040516024016130e09190615ca4565b60408051601f198184030181529190526020810180516001600160e01b0316633907753760e01b17905287866113886084613903565b9250925092508261313c578160405163e1cd550960e01b81526004016107cb9190614049565b815160201461316b578151604051631e3be00960e21b81526020600482015260248101919091526044016107cb565b6000828060200190518101906131819190615d70565b9050866001600160a01b03168c6001600160a01b0316146131fd5760006131b28d8a6131ad868a6156df565b613820565b509050868110806131cc5750816131c988836156df565b14155b156131fb5760405163a966e21f60e01b81526004810183905260248101889052604481018290526064016107cb565b505b604080518082019091526001600160a01b039098168852602088015250949550505050505095945050505050565b600061323e826301ffc9a760e01b613276565b8015610d61575061326f827fffffffff00000000000000000000000000000000000000000000000000000000613276565b1592915050565b6040517fffffffff0000000000000000000000000000000000000000000000000000000082166024820152600090819060440160408051601f19818403018152919052602080820180516001600160e01b03166301ffc9a760e01b178152825192935060009283928392909183918a617530fa92503d91506000519050828015613301575060208210155b801561330d5750600081115b979650505050505050565b60005b8151811015610ff15760ff83166000908152600360205260408120835190919084908490811061334d5761334d6153f5565b6020908102919091018101516001600160a01b03168252810191909152604001600020805461ffff1916905560010161331b565b60005b8251811015610ab15760008382815181106133a1576133a16153f5565b60200260200101519050600060028111156133be576133be614319565b60ff80871660009081526003602090815260408083206001600160a01b038716845290915290205461010090041660028111156133fd576133fd614319565b1461341e576004604051631b3fab5160e11b81526004016107cb9190615afe565b6001600160a01b0381166134455760405163d6c62c9b60e01b815260040160405180910390fd5b60405180604001604052808360ff16815260200184600281111561346b5761346b614319565b905260ff80871660009081526003602090815260408083206001600160a01b0387168452825290912083518154931660ff198416811782559184015190929091839161ffff1916176101008360028111156134c8576134c8614319565b021790555090505050806001019050613384565b60ff81166105445760ff8082166000908152600260205260409020600101546201000090041661351f57604051631e8ed32560e21b815260040160405180910390fd5b600b805467ffffffffffffffff1916905550565b600081815260018301602052604081205461357a57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610d61565b506000610d61565b8151805160608085015190830151608080870151940151604051600095869588956135e695919490939192916020019485526001600160a01b039390931660208501526001600160401b039182166040850152606084015216608082015260a00190565b604051602081830303815290604052805190602001208560200151805190602001208660400151805190602001208760a001516040516020016136299190615e2a565b60408051601f198184030181528282528051602091820120908301979097528101949094526060840192909252608083015260a082015260c081019190915260e0015b60405160208183030381529060405280519060200120905092915050565b6000806136988585856139dd565b6001600160401b0387166000908152600a6020908152604080832093835292905220549150505b949350505050565b600060026136d6608085615708565b6001600160401b03166136e9919061572e565b905060006136f78585611d35565b905081613706600160046156df565b901b19168183600381111561371d5761371d614319565b6001600160401b03871660009081526009602052604081209190921b9290921791829161374b608088615a77565b6001600160401b031681526020810191909152604001600020555050505050565b604051630304c3e160e51b815260009060609030906360987c209061379990889088908890600401615ec1565b600060405180830381600087803b1580156137b357600080fd5b505af19250505080156137c4575060015b613803573d8080156137f2576040519150601f19603f3d011682016040523d82523d6000602084013e6137f7565b606091505b50600392509050613818565b50506040805160208101909152600081526002905b935093915050565b60008060008060006138818860405160240161384b91906001600160a01b0391909116815260200190565b60408051601f198184030181529190526020810180516001600160e01b03166370a0823160e01b17905288886113886084613903565b925092509250826138a7578160405163e1cd550960e01b81526004016107cb9190614049565b60208251146138d6578151604051631e3be00960e21b81526020600482015260248101919091526044016107cb565b818060200190518101906138ea9190615d70565b6138f482886156df565b94509450505050935093915050565b6000606060008361ffff166001600160401b0381111561392557613925613d1d565b6040519080825280601f01601f19166020018201604052801561394f576020820181803683370190505b509150863b6139695763030ed58f60e21b60005260046000fd5b5a8581101561398357632be8ca8b60e21b60005260046000fd5b85900360408104810387106139a3576337c3be2960e01b60005260046000fd5b505a6000808a5160208c0160008c8cf193505a900390503d848111156139c65750835b808352806000602085013e50955095509592505050565b8251825160009190818303613a0557604051630469ac9960e21b815260040160405180910390fd5b6101018211801590613a1957506101018111155b613a36576040516309bde33960e01b815260040160405180910390fd5b60001982820101610100811115613a60576040516309bde33960e01b815260040160405180910390fd5b80600003613a8d5786600081518110613a7b57613a7b6153f5565b60200260200101519350505050613c5b565b6000816001600160401b03811115613aa757613aa7613d1d565b604051908082528060200260200182016040528015613ad0578160200160208202803683370190505b50905060008080805b85811015613bfa5760006001821b8b811603613b345788851015613b1d578c5160018601958e918110613b0e57613b0e6153f5565b60200260200101519050613b56565b8551600185019487918110613b0e57613b0e6153f5565b8b5160018401938d918110613b4b57613b4b6153f5565b602002602001015190505b600089861015613b86578d5160018701968f918110613b7757613b776153f5565b60200260200101519050613ba8565b8651600186019588918110613b9d57613b9d6153f5565b602002602001015190505b82851115613bc9576040516309bde33960e01b815260040160405180910390fd5b613bd38282613c62565b878481518110613be557613be56153f5565b60209081029190910101525050600101613ad9565b506001850382148015613c0c57508683145b8015613c1757508581145b613c34576040516309bde33960e01b815260040160405180910390fd5b836001860381518110613c4957613c496153f5565b60200260200101519750505050505050505b9392505050565b6000818310613c7a57613c758284613c80565b610d5e565b610d5e83835b60408051600160208201529081018390526060810182905260009060800161366c565b828054828255906000526020600020908101928215613cf8579160200282015b82811115613cf857825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190613cc3565b50613d04929150613d08565b5090565b5b80821115613d045760008155600101613d09565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b0381118282101715613d5557613d55613d1d565b60405290565b60405160a081016001600160401b0381118282101715613d5557613d55613d1d565b60405160c081016001600160401b0381118282101715613d5557613d55613d1d565b604080519081016001600160401b0381118282101715613d5557613d55613d1d565b604051601f8201601f191681016001600160401b0381118282101715613de957613de9613d1d565b604052919050565b60006001600160401b03821115613e0a57613e0a613d1d565b5060051b60200190565b6001600160a01b038116811461054457600080fd5b80356001600160401b0381168114613e4057600080fd5b919050565b801515811461054457600080fd5b8035613e4081613e45565b60006001600160401b03821115613e7757613e77613d1d565b50601f01601f191660200190565b600082601f830112613e9657600080fd5b8135613ea9613ea482613e5e565b613dc1565b818152846020838601011115613ebe57600080fd5b816020850160208301376000918101602001919091529392505050565b60006020808385031215613eee57600080fd5b82356001600160401b0380821115613f0557600080fd5b818501915085601f830112613f1957600080fd5b8135613f27613ea482613df1565b81815260059190911b83018401908481019088831115613f4657600080fd5b8585015b83811015613fec57803585811115613f625760008081fd5b86016080818c03601f1901811315613f7a5760008081fd5b613f82613d33565b89830135613f8f81613e14565b81526040613f9e848201613e29565b8b830152606080850135613fb181613e45565b83830152928401359289841115613fca57600091508182fd5b613fd88f8d86880101613e85565b908301525085525050918601918601613f4a565b5098975050505050505050565b60005b83811015614014578181015183820152602001613ffc565b50506000910152565b60008151808452614035816020860160208601613ff9565b601f01601f19169290920160200192915050565b602081526000610d5e602083018461401d565b8060608101831015610d6157600080fd5b60008083601f84011261407f57600080fd5b5081356001600160401b0381111561409657600080fd5b6020830191508360208285010111156140ae57600080fd5b9250929050565b60008083601f8401126140c757600080fd5b5081356001600160401b038111156140de57600080fd5b6020830191508360208260051b85010111156140ae57600080fd5b60008060008060008060008060e0898b03121561411557600080fd5b61411f8a8a61405c565b975060608901356001600160401b038082111561413b57600080fd5b6141478c838d0161406d565b909950975060808b013591508082111561416057600080fd5b61416c8c838d016140b5565b909750955060a08b013591508082111561418557600080fd5b506141928b828c016140b5565b999c989b50969995989497949560c00135949350505050565b6000806000608084860312156141c057600080fd5b6141ca858561405c565b925060608401356001600160401b038111156141e557600080fd5b6141f18682870161406d565b9497909650939450505050565b6001600160a01b0381511682526020810151151560208301526001600160401b03604082015116604083015260006060820151608060608501526136bf608085018261401d565b604080825283519082018190526000906020906060840190828701845b828110156142875781516001600160401b031684529284019290840190600101614262565b50505083810382850152845180825282820190600581901b8301840187850160005b838110156142d757601f198684030185526142c58383516141fe565b948701949250908601906001016142a9565b50909998505050505050505050565b600080604083850312156142f957600080fd5b61430283613e29565b915061431060208401613e29565b90509250929050565b634e487b7160e01b600052602160045260246000fd5b6004811061433f5761433f614319565b9052565b60208101610d61828461432f565b600060a0828403121561436357600080fd5b61436b613d5b565b90508135815261437d60208301613e29565b602082015261438e60408301613e29565b604082015261439f60608301613e29565b60608201526143b060808301613e29565b608082015292915050565b8035613e4081613e14565b803563ffffffff81168114613e4057600080fd5b600082601f8301126143eb57600080fd5b813560206143fb613ea483613df1565b82815260059290921b8401810191818101908684111561441a57600080fd5b8286015b848110156144ea5780356001600160401b038082111561443e5760008081fd5b9088019060a0828b03601f19018113156144585760008081fd5b614460613d5b565b87840135838111156144725760008081fd5b6144808d8a83880101613e85565b82525060408085013561449281613e14565b828a015260606144a38682016143c6565b828401526080915081860135858111156144bd5760008081fd5b6144cb8f8c838a0101613e85565b918401919091525091909301359083015250835291830191830161441e565b509695505050505050565b6000610140828403121561450857600080fd5b614510613d7d565b905061451c8383614351565b815260a08201356001600160401b038082111561453857600080fd5b61454485838601613e85565b602084015260c084013591508082111561455d57600080fd5b61456985838601613e85565b604084015261457a60e085016143bb565b6060840152610100840135608084015261012084013591508082111561459f57600080fd5b506145ac848285016143da565b60a08301525092915050565b600082601f8301126145c957600080fd5b813560206145d9613ea483613df1565b82815260059290921b840181019181810190868411156145f857600080fd5b8286015b848110156144ea5780356001600160401b0381111561461b5760008081fd5b6146298986838b01016144f5565b8452509183019183016145fc565b600082601f83011261464857600080fd5b81356020614658613ea483613df1565b82815260059290921b8401810191818101908684111561467757600080fd5b8286015b848110156144ea5780356001600160401b038082111561469a57600080fd5b818901915089603f8301126146ae57600080fd5b858201356146be613ea482613df1565b81815260059190911b830160400190878101908c8311156146de57600080fd5b604085015b83811015614717578035858111156146fa57600080fd5b6147098f6040838a0101613e85565b8452509189019189016146e3565b5087525050509284019250830161467b565b600082601f83011261473a57600080fd5b8135602061474a613ea483613df1565b8083825260208201915060208460051b87010193508684111561476c57600080fd5b602086015b848110156144ea5780358352918301918301614771565b600082601f83011261479957600080fd5b813560206147a9613ea483613df1565b82815260059290921b840181019181810190868411156147c857600080fd5b8286015b848110156144ea5780356001600160401b03808211156147ec5760008081fd5b9088019060a0828b03601f19018113156148065760008081fd5b61480e613d5b565b614819888501613e29565b81526040808501358481111561482f5760008081fd5b61483d8e8b838901016145b8565b8a84015250606080860135858111156148565760008081fd5b6148648f8c838a0101614637565b838501525060809150818601358581111561487f5760008081fd5b61488d8f8c838a0101614729565b91840191909152509190930135908301525083529183019183016147cc565b600080604083850312156148bf57600080fd5b6001600160401b03833511156148d457600080fd5b6148e18484358501614788565b91506001600160401b03602084013511156148fb57600080fd5b6020830135830184601f82011261491157600080fd5b61491e613ea48235613df1565b81358082526020808301929160051b84010187101561493c57600080fd5b602083015b6020843560051b850101811015614ae2576001600160401b038135111561496757600080fd5b87603f82358601011261497957600080fd5b61498c613ea46020833587010135613df1565b81358501602081810135808452908301929160059190911b016040018a10156149b457600080fd5b604083358701015b83358701602081013560051b01604001811015614ad2576001600160401b03813511156149e857600080fd5b833587018135016040818d03603f19011215614a0357600080fd5b614a0b613d9f565b604082013581526001600160401b0360608301351115614a2a57600080fd5b8c605f606084013584010112614a3f57600080fd5b6040606083013583010135614a56613ea482613df1565b808282526020820191508f60608460051b6060880135880101011115614a7b57600080fd5b6060808601358601015b60608460051b606088013588010101811015614ab257614aa4816143c6565b835260209283019201614a85565b5080602085015250505080855250506020830192506020810190506149bc565b5084525060209283019201614941565b508093505050509250929050565b600080600080600060608688031215614b0857600080fd5b85356001600160401b0380821115614b1f57600080fd5b614b2b89838a016144f5565b96506020880135915080821115614b4157600080fd5b614b4d89838a016140b5565b90965094506040880135915080821115614b6657600080fd5b50614b73888289016140b5565b969995985093965092949392505050565b600060608284031215614b9657600080fd5b604051606081018181106001600160401b0382111715614bb857614bb8613d1d565b6040528235614bc681613e14565b8152614bd4602084016143c6565b60208201526040830135614be781613e14565b60408201529392505050565b600060208284031215614c0557600080fd5b81356001600160401b03811115614c1b57600080fd5b820160a08185031215613c5b57600080fd5b803560ff81168114613e4057600080fd5b600060208284031215614c5057600080fd5b610d5e82614c2d565b60008151808452602080850194506020840160005b83811015614c935781516001600160a01b031687529582019590820190600101614c6e565b509495945050505050565b60208152600082518051602084015260ff602082015116604084015260ff604082015116606084015260608101511515608084015250602083015160c060a0840152614ced60e0840182614c59565b90506040840151601f198483030160c0850152614d0a8282614c59565b95945050505050565b60008060408385031215614d2657600080fd5b614d2f83613e29565b946020939093013593505050565b600060208284031215614d4f57600080fd5b610d5e82613e29565b602081526000610d5e60208301846141fe565b600060208284031215614d7d57600080fd5b8135613c5b81613e14565b600082601f830112614d9957600080fd5b81356020614da9613ea483613df1565b8083825260208201915060208460051b870101935086841115614dcb57600080fd5b602086015b848110156144ea578035614de381613e14565b8352918301918301614dd0565b60006020808385031215614e0357600080fd5b82356001600160401b0380821115614e1a57600080fd5b818501915085601f830112614e2e57600080fd5b8135614e3c613ea482613df1565b81815260059190911b83018401908481019088831115614e5b57600080fd5b8585015b83811015613fec57803585811115614e7657600080fd5b860160c0818c03601f19011215614e8d5760008081fd5b614e95613d7d565b8882013581526040614ea8818401614c2d565b8a8301526060614eb9818501614c2d565b8284015260809150614ecc828501613e53565b9083015260a08381013589811115614ee45760008081fd5b614ef28f8d83880101614d88565b838501525060c0840135915088821115614f0c5760008081fd5b614f1a8e8c84870101614d88565b9083015250845250918601918601614e5f565b80356001600160e01b0381168114613e4057600080fd5b600082601f830112614f5557600080fd5b81356020614f65613ea483613df1565b82815260069290921b84018101918181019086841115614f8457600080fd5b8286015b848110156144ea5760408189031215614fa15760008081fd5b614fa9613d9f565b614fb282613e29565b8152614fbf858301614f2d565b81860152835291830191604001614f88565b600082601f830112614fe257600080fd5b81356020614ff2613ea483613df1565b82815260059290921b8401810191818101908684111561501157600080fd5b8286015b848110156144ea5780356001600160401b03808211156150355760008081fd5b9088019060a0828b03601f190181131561504f5760008081fd5b615057613d5b565b615062888501613e29565b8152604080850135848111156150785760008081fd5b6150868e8b83890101613e85565b8a840152506060935061509a848601613e29565b9082015260806150ab858201613e29565b93820193909352920135908201528352918301918301615015565b600082601f8301126150d757600080fd5b813560206150e7613ea483613df1565b82815260069290921b8401810191818101908684111561510657600080fd5b8286015b848110156144ea57604081890312156151235760008081fd5b61512b613d9f565b81358152848201358582015283529183019160400161510a565b6000602080838503121561515857600080fd5b82356001600160401b038082111561516f57600080fd5b908401906080828703121561518357600080fd5b61518b613d33565b82358281111561519a57600080fd5b830160408189038113156151ad57600080fd5b6151b5613d9f565b8235858111156151c457600080fd5b8301601f81018b136151d557600080fd5b80356151e3613ea482613df1565b81815260069190911b8201890190898101908d83111561520257600080fd5b928a01925b828410156152525785848f03121561521f5760008081fd5b615227613d9f565b843561523281613e14565b815261523f858d01614f2d565b818d0152825292850192908a0190615207565b84525050508287013591508482111561526a57600080fd5b6152768a838501614f44565b8188015283525050828401358281111561528f57600080fd5b61529b88828601614fd1565b858301525060408301359350818411156152b457600080fd5b6152c0878585016150c6565b6040820152606083013560608201528094505050505092915050565b600082825180855260208086019550808260051b84010181860160005b8481101561536d57601f19868403018952815160a06001600160401b038083511686528683015182888801526153318388018261401d565b604085810151841690890152606080860151909316928801929092525060809283015192909501919091525097830197908301906001016152f9565b5090979650505050505050565b6001600160a01b03851681526000602060808184015261539d60808401876152dc565b83810360408581019190915286518083528388019284019060005b818110156153dd578451805184528601518684015293850193918301916001016153b8565b50508094505050505082606083015295945050505050565b634e487b7160e01b600052603260045260246000fd5b805160408084528151848201819052600092602091908201906060870190855b8181101561546257835180516001600160a01b031684528501516001600160e01b031685840152928401929185019160010161542b565b50508583015187820388850152805180835290840192506000918401905b808310156154bb57835180516001600160401b031683528501516001600160e01b031685830152928401926001929092019190850190615480565b50979650505050505050565b602081526000610d5e602083018461540b565b6000602082840312156154ec57600080fd5b8151613c5b81613e45565b600181811c9082168061550b57607f821691505b60208210810361552b57634e487b7160e01b600052602260045260246000fd5b50919050565b600080835461553f816154f7565b60018281168015615557576001811461556c5761559b565b60ff198416875282151583028701945061559b565b8760005260208060002060005b858110156155925781548a820152908401908201615579565b50505082870194505b50929695505050505050565b600081546155b4816154f7565b8085526020600183811680156155d157600181146155eb57615619565b60ff1985168884015283151560051b880183019550615619565b866000528260002060005b858110156156115781548a82018601529083019084016155f6565b890184019650505b505050505092915050565b604081526000615637604083018561401d565b8281036020840152614d0a81856155a7565b634e487b7160e01b600052601160045260246000fd5b6001600160401b0381811683821601908082111561567f5761567f615649565b5092915050565b60408152600061569960408301856152dc565b8281036020840152614d0a818561540b565b6000602082840312156156bd57600080fd5b81356001600160401b038111156156d357600080fd5b6136bf84828501614788565b81810381811115610d6157610d61615649565b634e487b7160e01b600052601260045260246000fd5b60006001600160401b0380841680615722576157226156f2565b92169190910692915050565b8082028115828204841417610d6157610d61615649565b80518252600060206001600160401b0381840151168185015260408084015160a0604087015261577860a087018261401d565b905060608501518682036060880152615791828261401d565b608087810151898303918a01919091528051808352908601935060009250908501905b808310156154bb57835180516001600160a01b03168352860151868301529285019260019290920191908401906157b4565b602081526000610d5e6020830184615745565b60808152600061580c6080830187615745565b61ffff9590951660208301525060408101929092526001600160a01b0316606090910152919050565b60008060006060848603121561584a57600080fd5b835161585581613e45565b60208501519093506001600160401b0381111561587157600080fd5b8401601f8101861361588257600080fd5b8051615890613ea482613e5e565b8181528760208385010111156158a557600080fd5b6158b6826020830160208601613ff9565b809450505050604084015190509250925092565b601f821115610ff1576000816000526020600020601f850160051c810160208610156158f35750805b601f850160051c820191505b81811015615912578281556001016158ff565b505050505050565b81516001600160401b0381111561593357615933613d1d565b6159478161594184546154f7565b846158ca565b602080601f83116001811461597c57600084156159645750858301515b600019600386901b1c1916600185901b178555615912565b600085815260208120601f198616915b828110156159ab5788860151825594840194600190910190840161598c565b50858210156159c95787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60208152600082546001600160a01b038116602084015260ff8160a01c16151560408401526001600160401b038160a81c16606084015250608080830152610d5e60a08301600185016155a7565b80820180821115610d6157610d61615649565b60ff8181168382160190811115610d6157610d61615649565b8183823760009101908152919050565b828152606082602083013760800192915050565b60006001600160401b0380841680615a9157615a916156f2565b92169190910492915050565b600060208284031215615aaf57600080fd5b610d5e826143c6565b6000808335601e19843603018112615acf57600080fd5b8301803591506001600160401b03821115615ae957600080fd5b6020019150368190038213156140ae57600080fd5b6020810160068310615b1257615b12614319565b91905290565b60ff818116838216029081169081811461567f5761567f615649565b600060a0820160ff881683526020878185015260a0604085015281875480845260c0860191508860005282600020935060005b81811015615b8c5784546001600160a01b031683526001948501949284019201615b67565b50508481036060860152865180825290820192508187019060005b81811015615bcc5782516001600160a01b031685529383019391830191600101615ba7565b50505060ff851660808501525090505b9695505050505050565b60006001600160401b03808616835280851660208401525060606040830152614d0a606083018461401d565b8281526040602082015260006136bf604083018461401d565b6001600160401b03848116825283166020820152606081016136bf604083018461432f565b848152615c60602082018561432f565b608060408201526000615c76608083018561401d565b905082606083015295945050505050565b600060208284031215615c9957600080fd5b8151613c5b81613e14565b6020815260008251610100806020850152615cc361012085018361401d565b91506020850151615cdf60408601826001600160401b03169052565b5060408501516001600160a01b038116606086015250606085015160808501526080850151615d1960a08601826001600160a01b03169052565b5060a0850151601f19808685030160c0870152615d36848361401d565b935060c08701519150808685030160e0870152615d53848361401d565b935060e0870151915080868503018387015250615bdc838261401d565b600060208284031215615d8257600080fd5b5051919050565b600082825180855260208086019550808260051b84010181860160005b8481101561536d57601f19868403018952815160a08151818652615dcc8287018261401d565b9150506001600160a01b03868301511686860152604063ffffffff8184015116818701525060608083015186830382880152615e08838261401d565b6080948501519790940196909652505098840198925090830190600101615da6565b602081526000610d5e6020830184615d89565b60008282518085526020808601955060208260051b8401016020860160005b8481101561536d57601f19868403018952615e7883835161401d565b98840198925090830190600101615e5c565b60008151808452602080850194506020840160005b83811015614c9357815163ffffffff1687529582019590820190600101615e9f565b60608152600084518051606084015260208101516001600160401b0380821660808601528060408401511660a08601528060608401511660c08601528060808401511660e0860152505050602085015161014080610100850152615f296101a085018361401d565b91506040870151605f198086850301610120870152615f48848361401d565b935060608901519150615f65838701836001600160a01b03169052565b608089015161016087015260a0890151925080868503016101808701525050615f8e8282615d89565b9150508281036020840152615fa38186615e3d565b90508281036040840152615bdc8185615e8a56fea164736f6c6343000818000a", } @@ -595,15 +595,15 @@ func (_OffRamp *OffRampTransactorSession) ExecuteSingleMessage(message InternalA return _OffRamp.Contract.ExecuteSingleMessage(&_OffRamp.TransactOpts, message, offchainTokenData, tokenGasOverrides) } -func (_OffRamp *OffRampTransactor) ManuallyExecute(opts *bind.TransactOpts, reports []InternalExecutionReportSingleChain, gasLimitOverrides [][]OffRampGasLimitOverride) (*types.Transaction, error) { +func (_OffRamp *OffRampTransactor) ManuallyExecute(opts *bind.TransactOpts, reports []InternalExecutionReport, gasLimitOverrides [][]OffRampGasLimitOverride) (*types.Transaction, error) { return _OffRamp.contract.Transact(opts, "manuallyExecute", reports, gasLimitOverrides) } -func (_OffRamp *OffRampSession) ManuallyExecute(reports []InternalExecutionReportSingleChain, gasLimitOverrides [][]OffRampGasLimitOverride) (*types.Transaction, error) { +func (_OffRamp *OffRampSession) ManuallyExecute(reports []InternalExecutionReport, gasLimitOverrides [][]OffRampGasLimitOverride) (*types.Transaction, error) { return _OffRamp.Contract.ManuallyExecute(&_OffRamp.TransactOpts, reports, gasLimitOverrides) } -func (_OffRamp *OffRampTransactorSession) ManuallyExecute(reports []InternalExecutionReportSingleChain, gasLimitOverrides [][]OffRampGasLimitOverride) (*types.Transaction, error) { +func (_OffRamp *OffRampTransactorSession) ManuallyExecute(reports []InternalExecutionReport, gasLimitOverrides [][]OffRampGasLimitOverride) (*types.Transaction, error) { return _OffRamp.Contract.ManuallyExecute(&_OffRamp.TransactOpts, reports, gasLimitOverrides) } @@ -2510,7 +2510,7 @@ type OffRampInterface interface { ExecuteSingleMessage(opts *bind.TransactOpts, message InternalAny2EVMRampMessage, offchainTokenData [][]byte, tokenGasOverrides []uint32) (*types.Transaction, error) - ManuallyExecute(opts *bind.TransactOpts, reports []InternalExecutionReportSingleChain, gasLimitOverrides [][]OffRampGasLimitOverride) (*types.Transaction, error) + ManuallyExecute(opts *bind.TransactOpts, reports []InternalExecutionReport, gasLimitOverrides [][]OffRampGasLimitOverride) (*types.Transaction, error) SetDynamicConfig(opts *bind.TransactOpts, dynamicConfig OffRampDynamicConfig) (*types.Transaction, error) diff --git a/core/gethwrappers/ccip/generated/report_codec/report_codec.go b/core/gethwrappers/ccip/generated/report_codec/report_codec.go index 06a169473b6..45e49035b05 100644 --- a/core/gethwrappers/ccip/generated/report_codec/report_codec.go +++ b/core/gethwrappers/ccip/generated/report_codec/report_codec.go @@ -52,7 +52,7 @@ type InternalAny2EVMTokenTransfer struct { Amount *big.Int } -type InternalExecutionReportSingleChain struct { +type InternalExecutionReport struct { SourceChainSelector uint64 Messages []InternalAny2EVMRampMessage OffchainTokenData [][][]byte @@ -99,7 +99,7 @@ type OffRampCommitReport struct { } var ReportCodecMetaData = &bind.MetaData{ - ABI: "[{\"anonymous\":false,\"inputs\":[{\"components\":[{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sourceToken\",\"type\":\"address\"},{\"internalType\":\"uint224\",\"name\":\"usdPerToken\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.TokenPriceUpdate[]\",\"name\":\"tokenPriceUpdates\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint224\",\"name\":\"usdPerUnitGas\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.GasPriceUpdate[]\",\"name\":\"gasPriceUpdates\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.PriceUpdates\",\"name\":\"priceUpdates\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRampAddress\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"maxSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structInternal.MerkleRoot[]\",\"name\":\"merkleRoots\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"internalType\":\"structIRMNRemote.Signature[]\",\"name\":\"rmnSignatures\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256\",\"name\":\"rmnRawVs\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"structOffRamp.CommitReport\",\"name\":\"report\",\"type\":\"tuple\"}],\"name\":\"CommitReportDecoded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"destTokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"destGasAmount\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.Any2EVMTokenTransfer[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.Any2EVMRampMessage[]\",\"name\":\"messages\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[][]\",\"name\":\"offchainTokenData\",\"type\":\"bytes[][]\"},{\"internalType\":\"bytes32[]\",\"name\":\"proofs\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"proofFlagBits\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"structInternal.ExecutionReportSingleChain[]\",\"name\":\"report\",\"type\":\"tuple[]\"}],\"name\":\"ExecuteReportDecoded\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"}],\"name\":\"decodeCommitReport\",\"outputs\":[{\"components\":[{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sourceToken\",\"type\":\"address\"},{\"internalType\":\"uint224\",\"name\":\"usdPerToken\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.TokenPriceUpdate[]\",\"name\":\"tokenPriceUpdates\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint224\",\"name\":\"usdPerUnitGas\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.GasPriceUpdate[]\",\"name\":\"gasPriceUpdates\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.PriceUpdates\",\"name\":\"priceUpdates\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRampAddress\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"maxSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structInternal.MerkleRoot[]\",\"name\":\"merkleRoots\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"internalType\":\"structIRMNRemote.Signature[]\",\"name\":\"rmnSignatures\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256\",\"name\":\"rmnRawVs\",\"type\":\"uint256\"}],\"internalType\":\"structOffRamp.CommitReport\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"}],\"name\":\"decodeExecuteReport\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"destTokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"destGasAmount\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.Any2EVMTokenTransfer[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.Any2EVMRampMessage[]\",\"name\":\"messages\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[][]\",\"name\":\"offchainTokenData\",\"type\":\"bytes[][]\"},{\"internalType\":\"bytes32[]\",\"name\":\"proofs\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"proofFlagBits\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.ExecutionReportSingleChain[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + ABI: "[{\"anonymous\":false,\"inputs\":[{\"components\":[{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sourceToken\",\"type\":\"address\"},{\"internalType\":\"uint224\",\"name\":\"usdPerToken\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.TokenPriceUpdate[]\",\"name\":\"tokenPriceUpdates\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint224\",\"name\":\"usdPerUnitGas\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.GasPriceUpdate[]\",\"name\":\"gasPriceUpdates\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.PriceUpdates\",\"name\":\"priceUpdates\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRampAddress\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"maxSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structInternal.MerkleRoot[]\",\"name\":\"merkleRoots\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"internalType\":\"structIRMNRemote.Signature[]\",\"name\":\"rmnSignatures\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256\",\"name\":\"rmnRawVs\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"structOffRamp.CommitReport\",\"name\":\"report\",\"type\":\"tuple\"}],\"name\":\"CommitReportDecoded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"destTokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"destGasAmount\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.Any2EVMTokenTransfer[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.Any2EVMRampMessage[]\",\"name\":\"messages\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[][]\",\"name\":\"offchainTokenData\",\"type\":\"bytes[][]\"},{\"internalType\":\"bytes32[]\",\"name\":\"proofs\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"proofFlagBits\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"structInternal.ExecutionReport[]\",\"name\":\"report\",\"type\":\"tuple[]\"}],\"name\":\"ExecuteReportDecoded\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"}],\"name\":\"decodeCommitReport\",\"outputs\":[{\"components\":[{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sourceToken\",\"type\":\"address\"},{\"internalType\":\"uint224\",\"name\":\"usdPerToken\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.TokenPriceUpdate[]\",\"name\":\"tokenPriceUpdates\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint224\",\"name\":\"usdPerUnitGas\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.GasPriceUpdate[]\",\"name\":\"gasPriceUpdates\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.PriceUpdates\",\"name\":\"priceUpdates\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRampAddress\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"maxSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structInternal.MerkleRoot[]\",\"name\":\"merkleRoots\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"internalType\":\"structIRMNRemote.Signature[]\",\"name\":\"rmnSignatures\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256\",\"name\":\"rmnRawVs\",\"type\":\"uint256\"}],\"internalType\":\"structOffRamp.CommitReport\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"}],\"name\":\"decodeExecuteReport\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"destTokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"destGasAmount\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.Any2EVMTokenTransfer[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.Any2EVMRampMessage[]\",\"name\":\"messages\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[][]\",\"name\":\"offchainTokenData\",\"type\":\"bytes[][]\"},{\"internalType\":\"bytes32[]\",\"name\":\"proofs\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"proofFlagBits\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.ExecutionReport[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", Bin: "0x608060405234801561001057600080fd5b506113e8806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80636fb349561461003b578063f816ec6014610064575b600080fd5b61004e61004936600461023a565b610084565b60405161005b91906104f7565b60405180910390f35b61007761007236600461023a565b6100a0565b60405161005b919061083c565b60608180602001905181019061009a9190610e85565b92915050565b6040805160c08101825260606080820181815260a08301829052825260208083018290529282018190526000908201528251909161009a9184018101908401611245565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160a0810167ffffffffffffffff81118282101715610136576101366100e4565b60405290565b60405160c0810167ffffffffffffffff81118282101715610136576101366100e4565b6040805190810167ffffffffffffffff81118282101715610136576101366100e4565b6040516080810167ffffffffffffffff81118282101715610136576101366100e4565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156101ec576101ec6100e4565b604052919050565b600067ffffffffffffffff82111561020e5761020e6100e4565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b60006020828403121561024c57600080fd5b813567ffffffffffffffff81111561026357600080fd5b8201601f8101841361027457600080fd5b8035610287610282826101f4565b6101a5565b81815285602083850101111561029c57600080fd5b81602084016020830137600091810160200191909152949350505050565b60005b838110156102d55781810151838201526020016102bd565b50506000910152565b600081518084526102f68160208601602086016102ba565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600082825180855260208086019550808260051b84010181860160005b848110156103f4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0868403018952815160a08151818652610389828701826102de565b91505073ffffffffffffffffffffffffffffffffffffffff868301511686860152604063ffffffff81840151168187015250606080830151868303828801526103d283826102de565b6080948501519790940196909652505098840198925090830190600101610345565b5090979650505050505050565b6000828251808552602080860195506005818360051b8501018287016000805b868110156104ac577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe088850381018c5283518051808752908801908887019080891b88018a01865b8281101561049557858a83030184526104838286516102de565b948c0194938c01939150600101610469565b509e8a019e97505050938701935050600101610421565b50919998505050505050505050565b60008151808452602080850194506020840160005b838110156104ec578151875295820195908201906001016104d0565b509495945050505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b828110156106df577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452815160a0860167ffffffffffffffff8083511688528883015160a08a8a015282815180855260c08b01915060c08160051b8c010194508b8301925060005b81811015610688577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff408c87030183528351805180518852868f820151168f890152866040820151166040890152866060820151166060890152866080820151166080890152508d81015161014060a089015261060b6101408901826102de565b9050604082015188820360c08a015261062482826102de565b915050606082015161064e60e08a018273ffffffffffffffffffffffffffffffffffffffff169052565b50608082015161010089015260a082015191508781036101208901526106748183610328565b97505050928c0192918c019160010161058b565b5050505050604082015187820360408901526106a48282610401565b915050606082015187820360608901526106be82826104bb565b6080938401519890930197909752509450928501929085019060010161051e565b5092979650505050505050565b60008151808452602080850194506020840160005b838110156104ec578151805167ffffffffffffffff1688528301517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff168388015260409096019590820190600101610701565b600082825180855260208086019550808260051b84010181860160005b848110156103f4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0868403018952815160a067ffffffffffffffff8083511686528683015182888801526107c4838801826102de565b6040858101518416908901526060808601519093169288019290925250608092830151929095019190915250978301979083019060010161076d565b60008151808452602080850194506020840160005b838110156104ec578151805188528301518388015260409096019590820190600101610815565b602080825282516080838301528051604060a08501819052815160e08601819052600094939284019185916101008801905b808410156108cb578451805173ffffffffffffffffffffffffffffffffffffffff1683528701517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff168783015293860193600193909301929082019061086e565b50938501518785037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600160c08901529361090581866106ec565b9450505050508185015191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0808583030160408601526109468284610750565b92506040860151915080858403016060860152506109648282610800565b915050606084015160808401528091505092915050565b600067ffffffffffffffff821115610995576109956100e4565b5060051b60200190565b805167ffffffffffffffff811681146109b757600080fd5b919050565b600060a082840312156109ce57600080fd5b6109d6610113565b9050815181526109e86020830161099f565b60208201526109f96040830161099f565b6040820152610a0a6060830161099f565b6060820152610a1b6080830161099f565b608082015292915050565b600082601f830112610a3757600080fd5b8151610a45610282826101f4565b818152846020838601011115610a5a57600080fd5b610a6b8260208301602087016102ba565b949350505050565b805173ffffffffffffffffffffffffffffffffffffffff811681146109b757600080fd5b600082601f830112610aa857600080fd5b81516020610ab86102828361097b565b82815260059290921b84018101918181019086841115610ad757600080fd5b8286015b84811015610bd357805167ffffffffffffffff80821115610afc5760008081fd5b818901915060a0807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848d03011215610b355760008081fd5b610b3d610113565b8784015183811115610b4f5760008081fd5b610b5d8d8a83880101610a26565b8252506040610b6d818601610a73565b8983015260608086015163ffffffff81168114610b8a5760008081fd5b808385015250608091508186015185811115610ba65760008081fd5b610bb48f8c838a0101610a26565b9184019190915250919093015190830152508352918301918301610adb565b509695505050505050565b600082601f830112610bef57600080fd5b81516020610bff6102828361097b565b82815260059290921b84018101918181019086841115610c1e57600080fd5b8286015b84811015610bd357805167ffffffffffffffff80821115610c435760008081fd5b8189019150610140807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848d03011215610c7d5760008081fd5b610c8561013c565b610c918c8986016109bc565b815260c084015183811115610ca65760008081fd5b610cb48d8a83880101610a26565b898301525060e084015183811115610ccc5760008081fd5b610cda8d8a83880101610a26565b604083015250610ced6101008501610a73565b60608201526101208401516080820152908301519082821115610d105760008081fd5b610d1e8c8984870101610a97565b60a08201528652505050918301918301610c22565b600082601f830112610d4457600080fd5b81516020610d546102828361097b565b82815260059290921b84018101918181019086841115610d7357600080fd5b8286015b84811015610bd357805167ffffffffffffffff80821115610d9757600080fd5b818901915089603f830112610dab57600080fd5b85820151610dbb6102828261097b565b81815260059190911b830160400190878101908c831115610ddb57600080fd5b604085015b83811015610e1457805185811115610df757600080fd5b610e068f6040838a0101610a26565b845250918901918901610de0565b50875250505092840192508301610d77565b600082601f830112610e3757600080fd5b81516020610e476102828361097b565b8083825260208201915060208460051b870101935086841115610e6957600080fd5b602086015b84811015610bd35780518352918301918301610e6e565b60006020808385031215610e9857600080fd5b825167ffffffffffffffff80821115610eb057600080fd5b818501915085601f830112610ec457600080fd5b8151610ed26102828261097b565b81815260059190911b83018401908481019088831115610ef157600080fd5b8585015b83811015610feb57805185811115610f0c57600080fd5b860160a0818c037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0011215610f415760008081fd5b610f49610113565b610f5489830161099f565b815260408083015188811115610f6a5760008081fd5b610f788e8c83870101610bde565b8b8401525060608084015189811115610f915760008081fd5b610f9f8f8d83880101610d33565b8385015250608091508184015189811115610fba5760008081fd5b610fc88f8d83880101610e26565b918401919091525060a09290920151918101919091528352918601918601610ef5565b5098975050505050505050565b80517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811681146109b757600080fd5b600082601f83011261103557600080fd5b815160206110456102828361097b565b82815260069290921b8401810191818101908684111561106457600080fd5b8286015b84811015610bd357604081890312156110815760008081fd5b61108961015f565b6110928261099f565b815261109f858301610ff8565b81860152835291830191604001611068565b600082601f8301126110c257600080fd5b815160206110d26102828361097b565b82815260059290921b840181019181810190868411156110f157600080fd5b8286015b84811015610bd357805167ffffffffffffffff808211156111165760008081fd5b818901915060a0807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848d0301121561114f5760008081fd5b611157610113565b61116288850161099f565b8152604080850151848111156111785760008081fd5b6111868e8b83890101610a26565b8a840152506060935061119a84860161099f565b9082015260806111ab85820161099f565b938201939093529201519082015283529183019183016110f5565b600082601f8301126111d757600080fd5b815160206111e76102828361097b565b82815260069290921b8401810191818101908684111561120657600080fd5b8286015b84811015610bd357604081890312156112235760008081fd5b61122b61015f565b81518152848201518582015283529183019160400161120a565b6000602080838503121561125857600080fd5b825167ffffffffffffffff8082111561127057600080fd5b908401906080828703121561128457600080fd5b61128c610182565b82518281111561129b57600080fd5b830160408189038113156112ae57600080fd5b6112b661015f565b8251858111156112c557600080fd5b8301601f81018b136112d657600080fd5b80516112e46102828261097b565b81815260069190911b8201890190898101908d83111561130357600080fd5b928a01925b828410156113515785848f0312156113205760008081fd5b61132861015f565b61133185610a73565b815261133e8c8601610ff8565b818d0152825292850192908a0190611308565b84525050508287015191508482111561136957600080fd5b6113758a838501611024565b8188015283525050828401518281111561138e57600080fd5b61139a888286016110b1565b858301525060408301519350818411156113b357600080fd5b6113bf878585016111c6565b604082015260608301516060820152809450505050509291505056fea164736f6c6343000818000a", } @@ -261,25 +261,25 @@ func (_ReportCodec *ReportCodecCallerSession) DecodeCommitReport(report []byte) return _ReportCodec.Contract.DecodeCommitReport(&_ReportCodec.CallOpts, report) } -func (_ReportCodec *ReportCodecCaller) DecodeExecuteReport(opts *bind.CallOpts, report []byte) ([]InternalExecutionReportSingleChain, error) { +func (_ReportCodec *ReportCodecCaller) DecodeExecuteReport(opts *bind.CallOpts, report []byte) ([]InternalExecutionReport, error) { var out []interface{} err := _ReportCodec.contract.Call(opts, &out, "decodeExecuteReport", report) if err != nil { - return *new([]InternalExecutionReportSingleChain), err + return *new([]InternalExecutionReport), err } - out0 := *abi.ConvertType(out[0], new([]InternalExecutionReportSingleChain)).(*[]InternalExecutionReportSingleChain) + out0 := *abi.ConvertType(out[0], new([]InternalExecutionReport)).(*[]InternalExecutionReport) return out0, err } -func (_ReportCodec *ReportCodecSession) DecodeExecuteReport(report []byte) ([]InternalExecutionReportSingleChain, error) { +func (_ReportCodec *ReportCodecSession) DecodeExecuteReport(report []byte) ([]InternalExecutionReport, error) { return _ReportCodec.Contract.DecodeExecuteReport(&_ReportCodec.CallOpts, report) } -func (_ReportCodec *ReportCodecCallerSession) DecodeExecuteReport(report []byte) ([]InternalExecutionReportSingleChain, error) { +func (_ReportCodec *ReportCodecCallerSession) DecodeExecuteReport(report []byte) ([]InternalExecutionReport, error) { return _ReportCodec.Contract.DecodeExecuteReport(&_ReportCodec.CallOpts, report) } @@ -461,7 +461,7 @@ func (it *ReportCodecExecuteReportDecodedIterator) Close() error { } type ReportCodecExecuteReportDecoded struct { - Report []InternalExecutionReportSingleChain + Report []InternalExecutionReport Raw types.Log } @@ -544,7 +544,7 @@ func (_ReportCodec *ReportCodec) Address() common.Address { type ReportCodecInterface interface { DecodeCommitReport(opts *bind.CallOpts, report []byte) (OffRampCommitReport, error) - DecodeExecuteReport(opts *bind.CallOpts, report []byte) ([]InternalExecutionReportSingleChain, error) + DecodeExecuteReport(opts *bind.CallOpts, report []byte) ([]InternalExecutionReport, error) FilterCommitReportDecoded(opts *bind.FilterOpts) (*ReportCodecCommitReportDecodedIterator, error) diff --git a/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 763dd0c7103..78c25cd4dc0 100644 --- a/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,21 +1,12 @@ GETH_VERSION: 1.13.8 burn_from_mint_token_pool: ../../../contracts/solc/v0.8.24/BurnFromMintTokenPool/BurnFromMintTokenPool.abi ../../../contracts/solc/v0.8.24/BurnFromMintTokenPool/BurnFromMintTokenPool.bin 1e60c28ad796a220a38043b369dec8d9bffe23e1c7d9895760e30672872afd06 burn_mint_token_pool: ../../../contracts/solc/v0.8.24/BurnMintTokenPool/BurnMintTokenPool.abi ../../../contracts/solc/v0.8.24/BurnMintTokenPool/BurnMintTokenPool.bin 3e8e3358f0bb520af069a7d37ea625940a88461a54418b1d5925eabced8c74df -burn_mint_token_pool_and_proxy: ../../../contracts/solc/v0.8.24/BurnMintTokenPoolAndProxy/BurnMintTokenPoolAndProxy.abi ../../../contracts/solc/v0.8.24/BurnMintTokenPoolAndProxy/BurnMintTokenPoolAndProxy.bin 717c079d5d13300cf3c3ee871c6e5bf9af904411f204fb081a9f3b263cca1391 -burn_with_from_mint_rebasing_token_pool: ../../../contracts/solc/v0.8.24/BurnWithFromMintRebasingTokenPool/BurnWithFromMintRebasingTokenPool.abi ../../../contracts/solc/v0.8.24/BurnWithFromMintRebasingTokenPool/BurnWithFromMintRebasingTokenPool.bin ec9b95105a33de14b078c1261d9cd9d6f3011e940a819b52f11a963951d4bb89 burn_with_from_mint_token_pool: ../../../contracts/solc/v0.8.24/BurnWithFromMintTokenPool/BurnWithFromMintTokenPool.abi ../../../contracts/solc/v0.8.24/BurnWithFromMintTokenPool/BurnWithFromMintTokenPool.bin 734c2a0ea8f1224b5f01ed849410209e74b4e3427e8bfddb8ff5dd8ead5f2d8d -burn_with_from_mint_token_pool_and_proxy: ../../../contracts/solc/v0.8.24/BurnWithFromMintTokenPoolAndProxy/BurnWithFromMintTokenPoolAndProxy.abi ../../../contracts/solc/v0.8.24/BurnWithFromMintTokenPoolAndProxy/BurnWithFromMintTokenPoolAndProxy.bin 1ed5c299f928529081dc01b7a46db2b5e6728001767863495a7675d8db99a9e2 ccip_encoding_utils: ../../../contracts/solc/v0.8.24/ICCIPEncodingUtils/ICCIPEncodingUtils.abi ../../../contracts/solc/v0.8.24/ICCIPEncodingUtils/ICCIPEncodingUtils.bin a074f2ecf2749a1d5afc4cd9bfa48677f09c2be4e076776f87c6feb767432ecb ccip_home: ../../../contracts/solc/v0.8.24/CCIPHome/CCIPHome.abi ../../../contracts/solc/v0.8.24/CCIPHome/CCIPHome.bin 079b70ad36b4a9522518df82f01bdb8480fb9bb8de5791ef17ea1ddf044814be -ccip_reader_tester: ../../../contracts/solc/v0.8.24/CCIPReaderTester/CCIPReaderTester.abi ../../../contracts/solc/v0.8.24/CCIPReaderTester/CCIPReaderTester.bin 1a446106c0c93a97c2c4201319d4d41c79f0ac2ff369c97c4f9cc42b5e8e5a3d -commit_store: ../../../contracts/solc/v0.8.24/CommitStore/CommitStore.abi ../../../contracts/solc/v0.8.24/CommitStore/CommitStore.bin 274d87db70b643e00ab0a7e7845bb4b791f3b613bfc87708d33fc5a8369e2a41 -commit_store_helper: ../../../contracts/solc/v0.8.24/CommitStoreHelper/CommitStoreHelper.abi ../../../contracts/solc/v0.8.24/CommitStoreHelper/CommitStoreHelper.bin f7128dcc2ee6dbcbc976288abcc16970ffb19b59412c5202ef6b259d2007f801 ether_sender_receiver: ../../../contracts/solc/v0.8.24/EtherSenderReceiver/EtherSenderReceiver.abi ../../../contracts/solc/v0.8.24/EtherSenderReceiver/EtherSenderReceiver.bin 09510a3f773f108a3c231e8d202835c845ded862d071ec54c4f89c12d868b8de -evm_2_evm_offramp: ../../../contracts/solc/v0.8.24/EVM2EVMOffRamp/EVM2EVMOffRamp.abi ../../../contracts/solc/v0.8.24/EVM2EVMOffRamp/EVM2EVMOffRamp.bin b0d77babbe635cd6ba04c2af049badc9e9d28a4b6ed6bb75f830ad902a618beb -evm_2_evm_onramp: ../../../contracts/solc/v0.8.24/EVM2EVMOnRamp/EVM2EVMOnRamp.abi ../../../contracts/solc/v0.8.24/EVM2EVMOnRamp/EVM2EVMOnRamp.bin 5c02c2b167946b3467636ff2bb58594cb4652fc63d8bdfee2488ed562e2a3e50 fee_quoter: ../../../contracts/solc/v0.8.24/FeeQuoter/FeeQuoter.abi ../../../contracts/solc/v0.8.24/FeeQuoter/FeeQuoter.bin 6806f01f305df73a923361f128b8962e9a8d3e7338a9a5b5fbe0636e6c9fc35f lock_release_token_pool: ../../../contracts/solc/v0.8.24/LockReleaseTokenPool/LockReleaseTokenPool.abi ../../../contracts/solc/v0.8.24/LockReleaseTokenPool/LockReleaseTokenPool.bin e6a8ec9e8faccb1da7d90e0f702ed72975964f97dc3222b54cfcca0a0ba3fea2 -lock_release_token_pool_and_proxy: ../../../contracts/solc/v0.8.24/LockReleaseTokenPoolAndProxy/LockReleaseTokenPoolAndProxy.abi ../../../contracts/solc/v0.8.24/LockReleaseTokenPoolAndProxy/LockReleaseTokenPoolAndProxy.bin e632b08be0fbd1d013e8b3a9d75293d0d532b83071c531ff2be1deec1fa48ec1 maybe_revert_message_receiver: ../../../contracts/solc/v0.8.24/MaybeRevertMessageReceiver/MaybeRevertMessageReceiver.abi ../../../contracts/solc/v0.8.24/MaybeRevertMessageReceiver/MaybeRevertMessageReceiver.bin d73956c26232ebcc4a5444429fa99cbefed960e323be9b5a24925885c2e477d5 message_hasher: ../../../contracts/solc/v0.8.24/MessageHasher/MessageHasher.abi ../../../contracts/solc/v0.8.24/MessageHasher/MessageHasher.bin ec2d3a92348d8e7b8f0d359b62a45157b9d2c750c01fbcf991826c4392f6e218 mock_usdc_token_messenger: ../../../contracts/solc/v0.8.24/MockE2EUSDCTokenMessenger/MockE2EUSDCTokenMessenger.abi ../../../contracts/solc/v0.8.24/MockE2EUSDCTokenMessenger/MockE2EUSDCTokenMessenger.bin d976651d36b33ac2196b32b9d2f4fa6690c6a18d41b621365659fce1c1d1e737 @@ -24,18 +15,15 @@ mock_v3_aggregator_contract: ../../../contracts/solc/v0.8.24/MockV3Aggregator/Mo multi_aggregate_rate_limiter: ../../../contracts/solc/v0.8.24/MultiAggregateRateLimiter/MultiAggregateRateLimiter.abi ../../../contracts/solc/v0.8.24/MultiAggregateRateLimiter/MultiAggregateRateLimiter.bin 0b541232e49727e947dc164eadf35963c66e67576f21baa0ecaa06a8833148ed multi_ocr3_helper: ../../../contracts/solc/v0.8.24/MultiOCR3Helper/MultiOCR3Helper.abi ../../../contracts/solc/v0.8.24/MultiOCR3Helper/MultiOCR3Helper.bin 04b6b261dd71925670bf4d904aaf7bf08543452009feefb88e07d4c49d12e969 nonce_manager: ../../../contracts/solc/v0.8.24/NonceManager/NonceManager.abi ../../../contracts/solc/v0.8.24/NonceManager/NonceManager.bin 6f64e1083b356c06ee66b9138e398b9c97a4cd3e8c9ec38cf3010cebc79af536 -offramp: ../../../contracts/solc/v0.8.24/OffRamp/OffRamp.abi ../../../contracts/solc/v0.8.24/OffRamp/OffRamp.bin cff099545fa50fcfd411c16c9cac754a0ba71ef2590840c0732d42604224200f +offramp: ../../../contracts/solc/v0.8.24/OffRamp/OffRamp.abi ../../../contracts/solc/v0.8.24/OffRamp/OffRamp.bin 2f58f2327fcfa80923fe8e8167eb1e692809e3b7163a7c3d7d2fa3650e914b79 onramp: ../../../contracts/solc/v0.8.24/OnRamp/OnRamp.abi ../../../contracts/solc/v0.8.24/OnRamp/OnRamp.bin 330be295a313aa4064819fb76d27f4b1a35fdcbd0959d68a71f1ebb72d0b1d7f ping_pong_demo: ../../../contracts/solc/v0.8.24/PingPongDemo/PingPongDemo.abi ../../../contracts/solc/v0.8.24/PingPongDemo/PingPongDemo.bin c1c2f8a65c7ffd971899cae7fe62f2da57d09e936151e2b92163c4bebe699d6b -price_registry: ../../../contracts/solc/v0.8.24/PriceRegistry/PriceRegistry.abi ../../../contracts/solc/v0.8.24/PriceRegistry/PriceRegistry.bin e7781d600c1bb7aa4620106af7f6e146a109b97f4cb6a7d06c9e15773340ecb2 registry_module_owner_custom: ../../../contracts/solc/v0.8.24/RegistryModuleOwnerCustom/RegistryModuleOwnerCustom.abi ../../../contracts/solc/v0.8.24/RegistryModuleOwnerCustom/RegistryModuleOwnerCustom.bin 75be86323c227917a9bbc3f799d7ed02f92db546653a36db30ed0ebe64461353 -report_codec: ../../../contracts/solc/v0.8.24/ReportCodec/ReportCodec.abi ../../../contracts/solc/v0.8.24/ReportCodec/ReportCodec.bin 738616a62c24ad4c5b851d174f40a06b51ccbc0bbfc65f04ff9d1c865f0be5bc -rmn_contract: ../../../contracts/solc/v0.8.24/RMN/RMN.abi ../../../contracts/solc/v0.8.24/RMN/RMN.bin 8b45b0fb08631c6b582fd3c0b4052a79cc2b4e091e6286af1ab131bef63661f9 +report_codec: ../../../contracts/solc/v0.8.24/ReportCodec/ReportCodec.abi ../../../contracts/solc/v0.8.24/ReportCodec/ReportCodec.bin 695b4798f1e9d6f1442f7e6d7c499d23464d9237f325effde132913c404607f7 rmn_home: ../../../contracts/solc/v0.8.24/RMNHome/RMNHome.abi ../../../contracts/solc/v0.8.24/RMNHome/RMNHome.bin fc2ba87d987b4e4d6e0c63b3ffb6d515fae1ab27778f6a0a6ed927d8d6aeaf8f rmn_proxy_contract: ../../../contracts/solc/v0.8.24/ARMProxy/ARMProxy.abi ../../../contracts/solc/v0.8.24/ARMProxy/ARMProxy.bin b048d8e752e3c41113ebb305c1efa06737ad36b4907b93e627fb0a3113023454 rmn_remote: ../../../contracts/solc/v0.8.24/RMNRemote/RMNRemote.abi ../../../contracts/solc/v0.8.24/RMNRemote/RMNRemote.bin 8d7f0e4581acac5c6b42c7cca1c06e699fdcf9dbca782dde153b7d190cdbe9bc router: ../../../contracts/solc/v0.8.24/Router/Router.abi ../../../contracts/solc/v0.8.24/Router/Router.bin 2e4f0a7826c8abb49d882bb49fc5ff20a186dbd3137624b9097ffed903ae4888 -self_funded_ping_pong: ../../../contracts/solc/v0.8.24/SelfFundedPingPong/SelfFundedPingPong.abi ../../../contracts/solc/v0.8.24/SelfFundedPingPong/SelfFundedPingPong.bin 8ea5d75dbc3f8afd90d22c4a665a94e02892259cd16520c1c6b4cf0dc80c9148 token_admin_registry: ../../../contracts/solc/v0.8.24/TokenAdminRegistry/TokenAdminRegistry.abi ../../../contracts/solc/v0.8.24/TokenAdminRegistry/TokenAdminRegistry.bin 942be7d1681ac102e0615bee13f76838ebb0b261697cf1270d2bf82c12e57aeb token_pool: ../../../contracts/solc/v0.8.24/TokenPool/TokenPool.abi ../../../contracts/solc/v0.8.24/TokenPool/TokenPool.bin 7c01fd89f5153baa4d7409d14beabb3f861abfbf8880d3c6d06802cc398570f9 usdc_reader_tester: ../../../contracts/solc/v0.8.24/USDCReaderTester/USDCReaderTester.abi ../../../contracts/solc/v0.8.24/USDCReaderTester/USDCReaderTester.bin 672a07c9218fd6ad7c04dde583088b0f5ffc8d55a46f4be1714008dd3409438b diff --git a/core/gethwrappers/ccip/go_generate.go b/core/gethwrappers/ccip/go_generate.go index cab2591693a..cbc91a9687f 100644 --- a/core/gethwrappers/ccip/go_generate.go +++ b/core/gethwrappers/ccip/go_generate.go @@ -2,15 +2,10 @@ // golang packages, using abigen. package ccip -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/CommitStore/CommitStore.abi ../../../contracts/solc/v0.8.24/CommitStore/CommitStore.bin CommitStore commit_store -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/CommitStoreHelper/CommitStoreHelper.abi ../../../contracts/solc/v0.8.24/CommitStoreHelper/CommitStoreHelper.bin CommitStoreHelper commit_store_helper -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/RMN/RMN.abi ../../../contracts/solc/v0.8.24/RMN/RMN.bin RMNContract rmn_contract //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/ARMProxy/ARMProxy.abi ../../../contracts/solc/v0.8.24/ARMProxy/ARMProxy.bin RMNProxyContract rmn_proxy_contract //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/TokenAdminRegistry/TokenAdminRegistry.abi ../../../contracts/solc/v0.8.24/TokenAdminRegistry/TokenAdminRegistry.bin TokenAdminRegistry token_admin_registry //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/RegistryModuleOwnerCustom/RegistryModuleOwnerCustom.abi ../../../contracts/solc/v0.8.24/RegistryModuleOwnerCustom/RegistryModuleOwnerCustom.bin RegistryModuleOwnerCustom registry_module_owner_custom -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/EVM2EVMOnRamp/EVM2EVMOnRamp.abi ../../../contracts/solc/v0.8.24/EVM2EVMOnRamp/EVM2EVMOnRamp.bin EVM2EVMOnRamp evm_2_evm_onramp //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/OnRamp/OnRamp.abi ../../../contracts/solc/v0.8.24/OnRamp/OnRamp.bin OnRamp onramp -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/EVM2EVMOffRamp/EVM2EVMOffRamp.abi ../../../contracts/solc/v0.8.24/EVM2EVMOffRamp/EVM2EVMOffRamp.bin EVM2EVMOffRamp evm_2_evm_offramp //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/OffRamp/OffRamp.abi ../../../contracts/solc/v0.8.24/OffRamp/OffRamp.bin OffRamp offramp //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/RMNRemote/RMNRemote.abi ../../../contracts/solc/v0.8.24/RMNRemote/RMNRemote.bin RMNRemote rmn_remote //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/RMNHome/RMNHome.abi ../../../contracts/solc/v0.8.24/RMNHome/RMNHome.bin RMNHome rmn_home @@ -24,22 +19,16 @@ package ccip //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/BurnMintTokenPool/BurnMintTokenPool.abi ../../../contracts/solc/v0.8.24/BurnMintTokenPool/BurnMintTokenPool.bin BurnMintTokenPool burn_mint_token_pool //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/BurnFromMintTokenPool/BurnFromMintTokenPool.abi ../../../contracts/solc/v0.8.24/BurnFromMintTokenPool/BurnFromMintTokenPool.bin BurnFromMintTokenPool burn_from_mint_token_pool //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/BurnWithFromMintTokenPool/BurnWithFromMintTokenPool.abi ../../../contracts/solc/v0.8.24/BurnWithFromMintTokenPool/BurnWithFromMintTokenPool.bin BurnWithFromMintTokenPool burn_with_from_mint_token_pool -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/BurnMintTokenPoolAndProxy/BurnMintTokenPoolAndProxy.abi ../../../contracts/solc/v0.8.24/BurnMintTokenPoolAndProxy/BurnMintTokenPoolAndProxy.bin BurnMintTokenPoolAndProxy burn_mint_token_pool_and_proxy -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/BurnWithFromMintTokenPoolAndProxy/BurnWithFromMintTokenPoolAndProxy.abi ../../../contracts/solc/v0.8.24/BurnWithFromMintTokenPoolAndProxy/BurnWithFromMintTokenPoolAndProxy.bin BurnWithFromMintTokenPoolAndProxy burn_with_from_mint_token_pool_and_proxy //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/LockReleaseTokenPool/LockReleaseTokenPool.abi ../../../contracts/solc/v0.8.24/LockReleaseTokenPool/LockReleaseTokenPool.bin LockReleaseTokenPool lock_release_token_pool -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/LockReleaseTokenPoolAndProxy/LockReleaseTokenPoolAndProxy.abi ../../../contracts/solc/v0.8.24/LockReleaseTokenPoolAndProxy/LockReleaseTokenPoolAndProxy.bin LockReleaseTokenPoolAndProxy lock_release_token_pool_and_proxy //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/TokenPool/TokenPool.abi ../../../contracts/solc/v0.8.24/TokenPool/TokenPool.bin TokenPool token_pool //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/USDCTokenPool/USDCTokenPool.abi ../../../contracts/solc/v0.8.24/USDCTokenPool/USDCTokenPool.bin USDCTokenPool usdc_token_pool -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/BurnWithFromMintRebasingTokenPool/BurnWithFromMintRebasingTokenPool.abi ../../../contracts/solc/v0.8.24/BurnWithFromMintRebasingTokenPool/BurnWithFromMintRebasingTokenPool.bin BurnWithFromMintRebasingTokenPool burn_with_from_mint_rebasing_token_pool // Helpers //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/MockV3Aggregator/MockV3Aggregator.abi ../../../contracts/solc/v0.8.24/MockV3Aggregator/MockV3Aggregator.bin MockV3Aggregator mock_v3_aggregator_contract //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/MaybeRevertMessageReceiver/MaybeRevertMessageReceiver.abi ../../../contracts/solc/v0.8.24/MaybeRevertMessageReceiver/MaybeRevertMessageReceiver.bin MaybeRevertMessageReceiver maybe_revert_message_receiver //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/PingPongDemo/PingPongDemo.abi ../../../contracts/solc/v0.8.24/PingPongDemo/PingPongDemo.bin PingPongDemo ping_pong_demo -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/SelfFundedPingPong/SelfFundedPingPong.abi ../../../contracts/solc/v0.8.24/SelfFundedPingPong/SelfFundedPingPong.bin SelfFundedPingPong self_funded_ping_pong //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/MessageHasher/MessageHasher.abi ../../../contracts/solc/v0.8.24/MessageHasher/MessageHasher.bin MessageHasher message_hasher //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/MultiOCR3Helper/MultiOCR3Helper.abi ../../../contracts/solc/v0.8.24/MultiOCR3Helper/MultiOCR3Helper.bin MultiOCR3Helper multi_ocr3_helper -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/CCIPReaderTester/CCIPReaderTester.abi ../../../contracts/solc/v0.8.24/CCIPReaderTester/CCIPReaderTester.bin CCIPReaderTester ccip_reader_tester //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/USDCReaderTester/USDCReaderTester.abi ../../../contracts/solc/v0.8.24/USDCReaderTester/USDCReaderTester.bin USDCReaderTester usdc_reader_tester //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/ReportCodec/ReportCodec.abi ../../../contracts/solc/v0.8.24/ReportCodec/ReportCodec.bin ReportCodec report_codec //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/EtherSenderReceiver/EtherSenderReceiver.abi ../../../contracts/solc/v0.8.24/EtherSenderReceiver/EtherSenderReceiver.bin EtherSenderReceiver ether_sender_receiver diff --git a/core/gethwrappers/keystone/generated/capabilities_registry/capabilities_registry.go b/core/gethwrappers/keystone/generated/capabilities_registry/capabilities_registry.go index 971d2264d61..bf8d7978a1c 100644 --- a/core/gethwrappers/keystone/generated/capabilities_registry/capabilities_registry.go +++ b/core/gethwrappers/keystone/generated/capabilities_registry/capabilities_registry.go @@ -88,8 +88,8 @@ type CapabilitiesRegistryNodeParams struct { } var CapabilitiesRegistryMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AccessForbidden\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityIsDeprecated\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"CapabilityRequiredByDON\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"DONDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONCapability\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONNode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedConfigurationContract\",\"type\":\"address\"}],\"name\":\"InvalidCapabilityConfigurationContractInterface\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"nodeCount\",\"type\":\"uint256\"}],\"name\":\"InvalidFaultTolerance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"name\":\"InvalidNodeCapabilities\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"encryptionPublicKey\",\"type\":\"bytes32\"}],\"name\":\"InvalidNodeEncryptionPublicKey\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeOperatorAdmin\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"InvalidNodeP2PId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"lengthOne\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lengthTwo\",\"type\":\"uint256\"}],\"name\":\"LengthMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"NodeAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"NodeDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"NodeDoesNotSupportCapability\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"}],\"name\":\"NodeOperatorDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"NodePartOfCapabilitiesDON\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"NodePartOfWorkflowDON\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityConfigured\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDeprecated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"}],\"name\":\"NodeAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"}],\"name\":\"NodeOperatorRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"NodeRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"}],\"name\":\"NodeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"labelledName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityType\",\"name\":\"capabilityType\",\"type\":\"uint8\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilitiesRegistry.Capability[]\",\"name\":\"capabilities\",\"type\":\"tuple[]\"}],\"name\":\"addCapabilities\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"acceptsWorkflows\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"}],\"name\":\"addDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilitiesRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"addNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"encryptionPublicKey\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilitiesRegistry.NodeParams[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"addNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"name\":\"deprecateCapabilities\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCapabilities\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"},{\"internalType\":\"string\",\"name\":\"labelledName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityType\",\"name\":\"capabilityType\",\"type\":\"uint8\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isDeprecated\",\"type\":\"bool\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"}],\"name\":\"getCapability\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"},{\"internalType\":\"string\",\"name\":\"labelledName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityType\",\"name\":\"capabilityType\",\"type\":\"uint8\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isDeprecated\",\"type\":\"bool\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityInfo\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"getCapabilityConfigs\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"getDON\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"id\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"acceptsWorkflows\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodeP2PIds\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"}],\"internalType\":\"structCapabilitiesRegistry.DONInfo\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDONs\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"id\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"acceptsWorkflows\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodeP2PIds\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"}],\"internalType\":\"structCapabilitiesRegistry.DONInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"labelledName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"}],\"name\":\"getHashedCapabilityId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"getNode\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"workflowDONId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"encryptionPublicKey\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256[]\",\"name\":\"capabilitiesDONIds\",\"type\":\"uint256[]\"}],\"internalType\":\"structCapabilitiesRegistry.NodeInfo\",\"name\":\"nodeInfo\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"}],\"name\":\"getNodeOperator\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilitiesRegistry.NodeOperator\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNodeOperators\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilitiesRegistry.NodeOperator[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNodes\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"workflowDONId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"encryptionPublicKey\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256[]\",\"name\":\"capabilitiesDONIds\",\"type\":\"uint256[]\"}],\"internalType\":\"structCapabilitiesRegistry.NodeInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"isCapabilityDeprecated\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"donIds\",\"type\":\"uint32[]\"}],\"name\":\"removeDONs\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint32[]\"}],\"name\":\"removeNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"removedNodeP2PIds\",\"type\":\"bytes32[]\"}],\"name\":\"removeNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"}],\"name\":\"updateDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint32[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilitiesRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"updateNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"encryptionPublicKey\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilitiesRegistry.NodeParams[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"updateNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6080604052600e80546001600160401b0319166401000000011790553480156200002857600080fd5b503380600081620000805760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000b357620000b381620000bc565b50505062000167565b336001600160a01b03821603620001165760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000077565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b61514880620001776000396000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c806350c946fe116100ee57806386fa424611610097578063d8bc7b6811610071578063d8bc7b68146103f6578063ddbe4f8214610409578063e29581aa1461041e578063f2fde38b1461043357600080fd5b806386fa42461461039b5780638da5cb5b146103ae5780639cb7c5f4146103d657600080fd5b8063715f5295116100c8578063715f52951461036d57806379ba50971461038057806384f5ed8a1461038857600080fd5b806350c946fe146103255780635d83d9671461034557806366acaa331461035857600080fd5b8063235374051161015b5780632c01a1e8116101355780632c01a1e8146102cb578063358039f4146102de578063398f3773146102f15780633f2a13c91461030457600080fd5b80632353740514610285578063275459f2146102a55780632a852933146102b857600080fd5b80631d05394c1161018c5780631d05394c1461023b578063214502431461025057806322bdbcbc1461026557600080fd5b80630fe5800a146101b357806312570011146101d9578063181f5a77146101fc575b600080fd5b6101c66101c1366004613f7c565b610446565b6040519081526020015b60405180910390f35b6101ec6101e7366004613fe0565b61047a565b60405190151581526020016101d0565b604080518082018252601a81527f4361706162696c6974696573526567697374727920312e302e30000000000000602082015290516101d09190614067565b61024e6102493660046140bf565b610487565b005b610258610694565b6040516101d09190614241565b6102786102733660046142dc565b6107f1565b6040516101d09190614334565b6102986102933660046142dc565b6108de565b6040516101d09190614347565b61024e6102b33660046140bf565b610922565b61024e6102c636600461437b565b6109f9565b61024e6102d93660046140bf565b610ad9565b61024e6102ec3660046140bf565b610d7c565b61024e6102ff3660046140bf565b6114a4565b61031761031236600461441d565b611663565b6040516101d0929190614447565b610338610333366004613fe0565b61184f565b6040516101d091906144e4565b61024e6103533660046140bf565b61193c565b610360611a31565b6040516101d091906144f7565b61024e61037b3660046140bf565b611c14565b61024e611cc6565b61024e6103963660046140bf565b611dc3565b61024e6103a936600461456c565b6122de565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101d0565b6103e96103e4366004613fe0565b61261e565b6040516101d091906146bb565b61024e6104043660046146ce565b612859565b610411612923565b6040516101d09190614754565b610426612a17565b6040516101d091906147c9565b61024e610441366004614862565b612b28565b6000828260405160200161045b929190614447565b6040516020818303038152906040528051906020012090505b92915050565b6000610474600583612b3c565b61048f612b57565b60005b8181101561068f5760008383838181106104ae576104ae61487d565b90506020020160208101906104c391906142dc565b63ffffffff8181166000908152600d60209081526040808320805464010000000081049095168085526001820190935290832094955093909290916a010000000000000000000090910460ff16905b61051b83612bda565b8110156105bb57811561057157600c60006105368584612be4565b8152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff1690556105b3565b6105b18663ffffffff16600c60006105928588612be490919063ffffffff16565b8152602001908152602001600020600501612bf090919063ffffffff16565b505b600101610512565b508354640100000000900463ffffffff16600003610612576040517f2b62be9b00000000000000000000000000000000000000000000000000000000815263ffffffff861660048201526024015b60405180910390fd5b63ffffffff85166000818152600d6020908152604080832080547fffffffffffffffffffffffffffffffffffffffffff0000000000000000000000169055519182527ff264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651910160405180910390a25050505050806001019050610492565b505050565b600e54606090640100000000900463ffffffff1660006106b56001836148db565b63ffffffff1667ffffffffffffffff8111156106d3576106d3613e39565b60405190808252806020026020018201604052801561075a57816020015b6040805160e081018252600080825260208083018290529282018190526060808301829052608083019190915260a0820181905260c082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816106f15790505b509050600060015b8363ffffffff168163ffffffff1610156107ce5763ffffffff8082166000908152600d602052604090205416156107c65761079c81612bfc565b8383815181106107ae576107ae61487d565b6020026020010181905250816107c3906148f8565b91505b600101610762565b506107da6001846148db565b63ffffffff1681146107ea578082525b5092915050565b60408051808201909152600081526060602082015263ffffffff82166000908152600b60209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff168352600181018054919284019161085590614930565b80601f016020809104026020016040519081016040528092919081815260200182805461088190614930565b80156108ce5780601f106108a3576101008083540402835291602001916108ce565b820191906000526020600020905b8154815290600101906020018083116108b157829003601f168201915b5050505050815250509050919050565b6040805160e0810182526000808252602082018190529181018290526060808201839052608082019290925260a0810182905260c081019190915261047482612bfc565b61092a612b57565b60005b63ffffffff811682111561068f57600083838363ffffffff168181106109555761095561487d565b905060200201602081019061096a91906142dc565b63ffffffff81166000908152600b6020526040812080547fffffffffffffffffffffffff00000000000000000000000000000000000000001681559192506109b56001830182613dcc565b505060405163ffffffff8216907fa59268ca81d40429e65ccea5385b59cf2d3fc6519371dee92f8eb1dae5107a7a90600090a2506109f281614983565b905061092d565b610a01612b57565b63ffffffff8088166000908152600d60205260408120805490926401000000009091041690819003610a67576040517f2b62be9b00000000000000000000000000000000000000000000000000000000815263ffffffff8a166004820152602401610609565b610ace888888886040518060a001604052808f63ffffffff16815260200187610a8f90614983565b63ffffffff811682528b15156020830152895460ff6a01000000000000000000009091048116151560408401528b166060909201919091529650612ec7565b505050505050505050565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015610d76576000848483818110610b1357610b1361487d565b602090810292909201356000818152600c90935260409092206001810154929350919050610b70576040517fd82f6adb00000000000000000000000000000000000000000000000000000000815260048101839052602401610609565b6000610b7e82600501612bda565b1115610bd357610b916005820184612be4565b6040517f60a6d89800000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015260248101839052604401610609565b805468010000000000000000900463ffffffff1615610c3b5780546040517f60b9df730000000000000000000000000000000000000000000000000000000081526801000000000000000090910463ffffffff16600482015260248101839052604401610609565b83158015610c755750805463ffffffff166000908152600b602052604090205473ffffffffffffffffffffffffffffffffffffffff163314155b15610cae576040517f9473075d000000000000000000000000000000000000000000000000000000008152336004820152602401610609565b6001810154610cbf90600790612bf0565b506002810154610cd190600990612bf0565b506000828152600c6020526040812080547fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001681556001810182905560028101829055600381018290559060058201818181610d2d8282613e06565b5050505050507f5254e609a97bab37b7cc79fe128f85c097bd6015c6e1624ae0ba392eb975320582604051610d6491815260200190565b60405180910390a15050600101610af7565b50505050565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015610d76576000848483818110610db657610db661487d565b9050602002810190610dc891906149a6565b610dd1906149e4565b6040808201516000908152600c6020908152828220805463ffffffff168352600b82528383208451808601909552805473ffffffffffffffffffffffffffffffffffffffff1685526001810180549697509195939493909284019190610e3690614930565b80601f0160208091040260200160405190810160405280929190818152602001828054610e6290614930565b8015610eaf5780601f10610e8457610100808354040283529160200191610eaf565b820191906000526020600020905b815481529060010190602001808311610e9257829003601f168201915b505050919092525050506001830154909150610eff5782604001516040517fd82f6adb00000000000000000000000000000000000000000000000000000000815260040161060991815260200190565b84158015610f245750805173ffffffffffffffffffffffffffffffffffffffff163314155b15610f5d576040517f9473075d000000000000000000000000000000000000000000000000000000008152336004820152602401610609565b6020830151610f98576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600182015460208401518114611019576020840151610fb990600790612b3c565b15610ff0576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208401516001840155611005600782612bf0565b506020840151611017906007906136de565b505b6080840151805160000361105b57806040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016106099190614ac1565b8354600090859060049061107c90640100000000900463ffffffff16614983565b91906101000a81548163ffffffff021916908363ffffffff1602179055905060005b8251811015611161576110d48382815181106110bc576110bc61487d565b60200260200101516003612b3c90919063ffffffff16565b61110c57826040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016106099190614ac1565b6111588382815181106111215761112161487d565b60200260200101518760040160008563ffffffff1663ffffffff1681526020019081526020016000206136de90919063ffffffff16565b5060010161109e565b50845468010000000000000000900463ffffffff1680156112c25763ffffffff8082166000908152600d6020908152604080832080546401000000009004909416835260019093018152828220600201805484518184028101840190955280855292939290918301828280156111f657602002820191906000526020600020905b8154815260200190600101908083116111e2575b5050505050905060005b81518110156112bf5761125582828151811061121e5761121e61487d565b60200260200101518960040160008763ffffffff1663ffffffff168152602001908152602001600020612b3c90919063ffffffff16565b6112b75781818151811061126b5761126b61487d565b6020026020010151836040517f03dcd86200000000000000000000000000000000000000000000000000000000815260040161060992919091825263ffffffff16602082015260400190565b600101611200565b50505b60006112d0876005016136ea565b905060005b81518163ffffffff161015611416576000828263ffffffff16815181106112fe576112fe61487d565b60209081029190910181015163ffffffff8082166000908152600d8452604080822080546401000000009004909316825260019092018452818120600201805483518187028101870190945280845293955090939192909183018282801561138557602002820191906000526020600020905b815481526020019060010190808311611371575b5050505050905060005b8151811015611402576113e48282815181106113ad576113ad61487d565b60200260200101518c60040160008a63ffffffff1663ffffffff168152602001908152602001600020612b3c90919063ffffffff16565b6113fa5781818151811061126b5761126b61487d565b60010161138f565b5050508061140f90614983565b90506112d5565b50875187547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff90911690811788556040808a015160028a018190556020808c01518351928352908201527f4b5b465e22eea0c3d40c30e936643245b80d19b2dcf75788c0699fe8d8db645b910160405180910390a25050505050505050806001019050610d9a565b6114ac612b57565b60005b8181101561068f5760008383838181106114cb576114cb61487d565b90506020028101906114dd9190614b05565b6114e690614b39565b805190915073ffffffffffffffffffffffffffffffffffffffff16611537576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600e54604080518082018252835173ffffffffffffffffffffffffffffffffffffffff908116825260208086015181840190815263ffffffff9095166000818152600b909252939020825181547fffffffffffffffffffffffff000000000000000000000000000000000000000016921691909117815592519192909160018201906115c39082614bf3565b5050600e80549091506000906115de9063ffffffff16614983565b91906101000a81548163ffffffff021916908363ffffffff160217905550816000015173ffffffffffffffffffffffffffffffffffffffff168163ffffffff167f78e94ca80be2c30abc061b99e7eb8583b1254781734b1e3ce339abb57da2fe8e84602001516040516116519190614067565b60405180910390a350506001016114af565b63ffffffff8083166000908152600d602090815260408083208054640100000000900490941680845260019094018252808320858452600301909152812080546060938493909290916116b590614930565b80601f01602080910402602001604051908101604052809291908181526020018280546116e190614930565b801561172e5780601f106117035761010080835404028352916020019161172e565b820191906000526020600020905b81548152906001019060200180831161171157829003601f168201915b5050506000888152600260208190526040909120015492935060609262010000900473ffffffffffffffffffffffffffffffffffffffff16159150611841905057600086815260026020819052604091829020015490517f8318ed5d00000000000000000000000000000000000000000000000000000000815263ffffffff891660048201526201000090910473ffffffffffffffffffffffffffffffffffffffff1690638318ed5d90602401600060405180830381865afa1580156117f8573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261183e9190810190614d0d565b90505b9093509150505b9250929050565b604080516101008101825260008082526020820181905291810182905260608082018390526080820183905260a082019290925260c0810182905260e081019190915260408051610100810182526000848152600c6020908152838220805463ffffffff8082168652640100000000820481168487018190526801000000000000000090920416858701526001820154606086015260028201546080860152600382015460a0860152835260040190529190912060c0820190611911906136ea565b8152602001611934600c60008681526020019081526020016000206005016136ea565b905292915050565b611944612b57565b60005b8181101561068f5760008383838181106119635761196361487d565b905060200201359050611980816003612b3c90919063ffffffff16565b6119b9576040517fe181733f00000000000000000000000000000000000000000000000000000000815260048101829052602401610609565b6119c46005826136de565b6119fd576040517ff7d7a29400000000000000000000000000000000000000000000000000000000815260048101829052602401610609565b60405181907fdcea1b78b6ddc31592a94607d537543fcaafda6cc52d6d5cc7bbfca1422baf2190600090a250600101611947565b600e5460609063ffffffff166000611a4a6001836148db565b63ffffffff1667ffffffffffffffff811115611a6857611a68613e39565b604051908082528060200260200182016040528015611aae57816020015b604080518082019091526000815260606020820152815260200190600190039081611a865790505b509050600060015b8363ffffffff168163ffffffff161015611bfe5763ffffffff81166000908152600b602052604090205473ffffffffffffffffffffffffffffffffffffffff1615611bf65763ffffffff81166000908152600b60209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff1683526001810180549192840191611b4a90614930565b80601f0160208091040260200160405190810160405280929190818152602001828054611b7690614930565b8015611bc35780601f10611b9857610100808354040283529160200191611bc3565b820191906000526020600020905b815481529060010190602001808311611ba657829003601f168201915b505050505081525050838381518110611bde57611bde61487d565b602002602001018190525081611bf3906148f8565b91505b600101611ab6565b50600e546107da9060019063ffffffff166148db565b611c1c612b57565b60005b8181101561068f576000838383818110611c3b57611c3b61487d565b9050602002810190611c4d91906149a6565b611c5690614d8a565b90506000611c6c82600001518360200151610446565b9050611c796003826136de565b611cb2576040517febf5255100000000000000000000000000000000000000000000000000000000815260048101829052602401610609565b611cbc81836136f7565b5050600101611c1f565b60015473ffffffffffffffffffffffffffffffffffffffff163314611d47576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610609565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015610d76576000848483818110611dfd57611dfd61487d565b9050602002810190611e0f91906149a6565b611e18906149e4565b805163ffffffff166000908152600b602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff168252600181018054959650939491939092840191611e6e90614930565b80601f0160208091040260200160405190810160405280929190818152602001828054611e9a90614930565b8015611ee75780601f10611ebc57610100808354040283529160200191611ee7565b820191906000526020600020905b815481529060010190602001808311611eca57829003601f168201915b50505091909252505081519192505073ffffffffffffffffffffffffffffffffffffffff16611f4d5781516040517fadd9ae1e00000000000000000000000000000000000000000000000000000000815263ffffffff9091166004820152602401610609565b83158015611f725750805173ffffffffffffffffffffffffffffffffffffffff163314155b15611fab576040517f9473075d000000000000000000000000000000000000000000000000000000008152336004820152602401610609565b6040808301516000908152600c602052206001810154156120005782604001516040517f5461848300000000000000000000000000000000000000000000000000000000815260040161060991815260200190565b60408301516120435782604001516040517f64e2ee9200000000000000000000000000000000000000000000000000000000815260040161060991815260200190565b602083015115806120605750602083015161206090600790612b3c565b15612097576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608301516120da5782606001516040517f37d8976500000000000000000000000000000000000000000000000000000000815260040161060991815260200190565b6080830151805160000361211c57806040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016106099190614ac1565b8154829060049061213a90640100000000900463ffffffff16614983565b82546101009290920a63ffffffff818102199093169183160217909155825464010000000090041660005b8251811015612210576121838382815181106110bc576110bc61487d565b6121bb57826040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016106099190614ac1565b6122078382815181106121d0576121d061487d565b60200260200101518560040160008563ffffffff1663ffffffff1681526020019081526020016000206136de90919063ffffffff16565b50600101612165565b5060608501516003840155845183547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff9182161784556040860151600285015560208601516001850181905561227091600791906136de16565b506040850151612282906009906136de565b50845160408087015160208089015183519283529082015263ffffffff909216917f74becb12a5e8fd0e98077d02dfba8f647c9670c9df177e42c2418cf17a636f05910160405180910390a25050505050806001019050611de1565b828114612321576040517fab8b67c60000000000000000000000000000000000000000000000000000000081526004810184905260248101829052604401610609565b6000805473ffffffffffffffffffffffffffffffffffffffff16905b848110156126165760008686838181106123595761235961487d565b905060200201602081019061236e91906142dc565b63ffffffff81166000908152600b6020526040902080549192509073ffffffffffffffffffffffffffffffffffffffff166123dd576040517fadd9ae1e00000000000000000000000000000000000000000000000000000000815263ffffffff83166004820152602401610609565b60008686858181106123f1576123f161487d565b90506020028101906124039190614b05565b61240c90614b39565b805190915073ffffffffffffffffffffffffffffffffffffffff1661245d576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815473ffffffffffffffffffffffffffffffffffffffff16331480159061249a57503373ffffffffffffffffffffffffffffffffffffffff861614155b156124d3576040517f9473075d000000000000000000000000000000000000000000000000000000008152336004820152602401610609565b8051825473ffffffffffffffffffffffffffffffffffffffff908116911614158061254f575060208082015160405161250c9201614067565b60405160208183030381529060405280519060200120826001016040516020016125369190614e30565b6040516020818303038152906040528051906020012014155b1561260857805182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178255602081015160018301906125a99082614bf3565b50806000015173ffffffffffffffffffffffffffffffffffffffff168363ffffffff167f86f41145bde5dd7f523305452e4aad3685508c181432ec733d5f345009358a2883602001516040516125ff9190614067565b60405180910390a35b50505080600101905061233d565b505050505050565b61265f6040805160e0810182526000808252606060208301819052928201839052909182019081526020016000815260006020820181905260409091015290565b6040805160e0810182528381526000848152600260209081529290208054919283019161268b90614930565b80601f01602080910402602001604051908101604052809291908181526020018280546126b790614930565b80156127045780601f106126d957610100808354040283529160200191612704565b820191906000526020600020905b8154815290600101906020018083116126e757829003601f168201915b5050505050815260200160026000858152602001908152602001600020600101805461272f90614930565b80601f016020809104026020016040519081016040528092919081815260200182805461275b90614930565b80156127a85780601f1061277d576101008083540402835291602001916127a8565b820191906000526020600020905b81548152906001019060200180831161278b57829003601f168201915b50505091835250506000848152600260208181526040909220015491019060ff1660038111156127da576127da6145d8565b815260008481526002602081815260409092200154910190610100900460ff16600181111561280b5761280b6145d8565b81526000848152600260208181526040928390209091015462010000900473ffffffffffffffffffffffffffffffffffffffff16908301520161284f600585612b3c565b1515905292915050565b612861612b57565b600e805460009164010000000090910463ffffffff1690600461288383614983565b82546101009290920a63ffffffff81810219909316918316021790915581166000818152600d602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001684179055815160a08101835292835260019083015286151590820152841515606082015260ff84166080820152909150612919908990899089908990612ec7565b5050505050505050565b6060600061293160036136ea565b90506000815167ffffffffffffffff81111561294f5761294f613e39565b6040519080825280602002602001820160405280156129c157816020015b6129ae6040805160e0810182526000808252606060208301819052928201839052909182019081526020016000815260006020820181905260409091015290565b81526020019060019003908161296d5790505b50905060005b82518110156107ea576129f28382815181106129e5576129e561487d565b602002602001015161261e565b828281518110612a0457612a0461487d565b60209081029190910101526001016129c7565b60606000612a2560096136ea565b90506000815167ffffffffffffffff811115612a4357612a43613e39565b604051908082528060200260200182016040528015612ad257816020015b60408051610100810182526000808252602080830182905292820181905260608083018290526080830182905260a083019190915260c0820181905260e082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909201910181612a615790505b50905060005b82518110156107ea57612b03838281518110612af657612af661487d565b602002602001015161184f565b828281518110612b1557612b1561487d565b6020908102919091010152600101612ad8565b612b30612b57565b612b39816138df565b50565b600081815260018301602052604081205415155b9392505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314612bd8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610609565b565b6000610474825490565b6000612b5083836139d4565b6000612b5083836139fe565b6040805160e0810182526000808252602080830182905282840182905260608084018390526080840183905260a0840181905260c084015263ffffffff8581168352600d8252848320805464010000000090049091168084526001909101825284832060028101805487518186028101860190985280885295969295919493909190830182828015612cad57602002820191906000526020600020905b815481526020019060010190808311612c99575b505050505090506000815167ffffffffffffffff811115612cd057612cd0613e39565b604051908082528060200260200182016040528015612d1657816020015b604080518082019091526000815260606020820152815260200190600190039081612cee5790505b50905060005b8151811015612e2e576040518060400160405280848381518110612d4257612d4261487d565b60200260200101518152602001856003016000868581518110612d6757612d6761487d565b602002602001015181526020019081526020016000208054612d8890614930565b80601f0160208091040260200160405190810160405280929190818152602001828054612db490614930565b8015612e015780601f10612dd657610100808354040283529160200191612e01565b820191906000526020600020905b815481529060010190602001808311612de457829003601f168201915b5050505050815250828281518110612e1b57612e1b61487d565b6020908102919091010152600101612d1c565b506040805160e08101825263ffffffff8089166000818152600d6020818152868320548086168752948b168187015260ff680100000000000000008604811697870197909752690100000000000000000085048716151560608701529290915290526a010000000000000000000090049091161515608082015260a08101612eb5856136ea565b81526020019190915295945050505050565b805163ffffffff9081166000908152600d602090815260408083208286015190941683526001909301905220608082015160ff161580612f19575060808201518590612f14906001614ede565b60ff16115b15612f625760808201516040517f25b4d61800000000000000000000000000000000000000000000000000000000815260ff909116600482015260248101869052604401610609565b6001826020015163ffffffff16111561304a57815163ffffffff166000908152600d602090815260408220908401516001918201918391612fa391906148db565b63ffffffff1663ffffffff168152602001908152602001600020905060005b612fcb82612bda565b81101561304757612ffa846000015163ffffffff16600c60006105928587600001612be490919063ffffffff16565b50600c60006130098484612be4565b8152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff169055600101612fc2565b50505b60005b858110156132845761307a87878381811061306a5761306a61487d565b85926020909102013590506136de565b6130db5782518787838181106130925761309261487d565b6040517f636e405700000000000000000000000000000000000000000000000000000000815263ffffffff90941660048501526020029190910135602483015250604401610609565b82606001511561323257825163ffffffff16600c60008989858181106131035761310361487d565b602090810292909201358352508101919091526040016000205468010000000000000000900463ffffffff161480159061317d5750600c600088888481811061314e5761314e61487d565b602090810292909201358352508101919091526040016000205468010000000000000000900463ffffffff1615155b156131df5782518787838181106131965761319661487d565b6040517f60b9df7300000000000000000000000000000000000000000000000000000000815263ffffffff90941660048501526020029190910135602483015250604401610609565b8251600c60008989858181106131f7576131f761487d565b90506020020135815260200190815260200160002060000160086101000a81548163ffffffff021916908363ffffffff16021790555061327c565b825161327a9063ffffffff16600c60008a8a868181106132545761325461487d565b9050602002013581526020019081526020016000206005016136de90919063ffffffff16565b505b60010161304d565b5060005b8381101561369257368585838181106132a3576132a361487d565b90506020028101906132b59190614b05565b90506132c360038235612b3c565b6132fc576040517fe181733f00000000000000000000000000000000000000000000000000000000815281356004820152602401610609565b61330860058235612b3c565b15613342576040517ff7d7a29400000000000000000000000000000000000000000000000000000000815281356004820152602401610609565b803560009081526003840160205260408120805461335f90614930565b905011156133ab5783516040517f3927d08000000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015281356024820152604401610609565b60005b878110156134b5576134528235600c60008c8c868181106133d1576133d161487d565b9050602002013581526020019081526020016000206004016000600c60008e8e888181106134015761340161487d565b90506020020135815260200190815260200160002060000160049054906101000a900463ffffffff1663ffffffff1663ffffffff168152602001908152602001600020612b3c90919063ffffffff16565b6134ad578888828181106134685761346861487d565b6040517fa7e792500000000000000000000000000000000000000000000000000000000081526020909102929092013560048301525082356024820152604401610609565b6001016133ae565b50600283018054600181018255600091825260209182902083359101556134de90820182614ef7565b823560009081526003860160205260409020916134fc919083614f5c565b50604080850151855163ffffffff9081166000908152600d602090815284822080549415156901000000000000000000027fffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffff90951694909417909355606088015188518316825284822080549115156a0100000000000000000000027fffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffff9092169190911790556080880151885183168252848220805460ff9290921668010000000000000000027fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff909216919091179055828801805189518416835294909120805494909216640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff9094169390931790558551915161368992918435908c908c9061364f90880188614ef7565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613af192505050565b50600101613288565b50815160208084015160405163ffffffff91821681529216917ff264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651910160405180910390a2505050505050565b6000612b508383613bd2565b60606000612b5083613c21565b608081015173ffffffffffffffffffffffffffffffffffffffff16156137995761374581608001517f78bea72100000000000000000000000000000000000000000000000000000000613c7d565b6137995760808101516040517fabb5e3fd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610609565b6000828152600260205260409020815182919081906137b89082614bf3565b50602082015160018201906137cd9082614bf3565b5060408201516002820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600183600381111561380f5761380f6145d8565b021790555060608201516002820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff16610100836001811115613856576138566145d8565b0217905550608091909101516002909101805473ffffffffffffffffffffffffffffffffffffffff90921662010000027fffffffffffffffffffff0000000000000000000000000000000000000000ffff90921691909117905560405182907f04f0a9bcf3f3a3b42a4d7ca081119755f82ebe43e0d30c8f7292c4fe0dc4a2ae90600090a25050565b3373ffffffffffffffffffffffffffffffffffffffff82160361395e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610609565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60008260000182815481106139eb576139eb61487d565b9060005260206000200154905092915050565b60008181526001830160205260408120548015613ae7576000613a22600183615077565b8554909150600090613a3690600190615077565b9050818114613a9b576000866000018281548110613a5657613a5661487d565b9060005260206000200154905080876000018481548110613a7957613a7961487d565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080613aac57613aac61508a565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610474565b6000915050610474565b6000848152600260208190526040909120015462010000900473ffffffffffffffffffffffffffffffffffffffff161561261657600084815260026020819052604091829020015490517ffba64a7c0000000000000000000000000000000000000000000000000000000081526201000090910473ffffffffffffffffffffffffffffffffffffffff169063fba64a7c90613b98908690869086908b908d906004016150b9565b600060405180830381600087803b158015613bb257600080fd5b505af1158015613bc6573d6000803e3d6000fd5b50505050505050505050565b6000818152600183016020526040812054613c1957508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610474565b506000610474565b606081600001805480602002602001604051908101604052809291908181526020018280548015613c7157602002820191906000526020600020905b815481526020019060010190808311613c5d575b50505050509050919050565b6000613c8883613c99565b8015612b505750612b508383613cfd565b6000613cc5827f01ffc9a700000000000000000000000000000000000000000000000000000000613cfd565b80156104745750613cf6827fffffffff00000000000000000000000000000000000000000000000000000000613cfd565b1592915050565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d91506000519050828015613db5575060208210155b8015613dc15750600081115b979650505050505050565b508054613dd890614930565b6000825580601f10613de8575050565b601f016020900490600052602060002090810190612b399190613e20565b5080546000825590600052602060002090810190612b3991905b5b80821115613e355760008155600101613e21565b5090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160a0810167ffffffffffffffff81118282101715613e8b57613e8b613e39565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613ed857613ed8613e39565b604052919050565b600067ffffffffffffffff821115613efa57613efa613e39565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f830112613f3757600080fd5b8135613f4a613f4582613ee0565b613e91565b818152846020838601011115613f5f57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215613f8f57600080fd5b823567ffffffffffffffff80821115613fa757600080fd5b613fb386838701613f26565b93506020850135915080821115613fc957600080fd5b50613fd685828601613f26565b9150509250929050565b600060208284031215613ff257600080fd5b5035919050565b60005b83811015614014578181015183820152602001613ffc565b50506000910152565b60008151808452614035816020860160208601613ff9565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000612b50602083018461401d565b60008083601f84011261408c57600080fd5b50813567ffffffffffffffff8111156140a457600080fd5b6020830191508360208260051b850101111561184857600080fd5b600080602083850312156140d257600080fd5b823567ffffffffffffffff8111156140e957600080fd5b6140f58582860161407a565b90969095509350505050565b60008151808452602080850194506020840160005b8381101561413257815187529582019590820190600101614116565b509495945050505050565b600082825180855260208086019550808260051b84010181860160005b848110156141ba578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001895281518051845284015160408585018190526141a68186018361401d565b9a86019a945050509083019060010161415a565b5090979650505050505050565b600063ffffffff8083511684528060208401511660208501525060ff604083015116604084015260608201511515606084015260808201511515608084015260a082015160e060a085015261421f60e0850182614101565b905060c083015184820360c0860152614238828261413d565b95945050505050565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156142b6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526142a48583516141c7565b9450928501929085019060010161426a565b5092979650505050505050565b803563ffffffff811681146142d757600080fd5b919050565b6000602082840312156142ee57600080fd5b612b50826142c3565b73ffffffffffffffffffffffffffffffffffffffff8151168252600060208201516040602085015261432c604085018261401d565b949350505050565b602081526000612b5060208301846142f7565b602081526000612b5060208301846141c7565b803580151581146142d757600080fd5b803560ff811681146142d757600080fd5b600080600080600080600060a0888a03121561439657600080fd5b61439f886142c3565b9650602088013567ffffffffffffffff808211156143bc57600080fd5b6143c88b838c0161407a565b909850965060408a01359150808211156143e157600080fd5b506143ee8a828b0161407a565b909550935061440190506060890161435a565b915061440f6080890161436a565b905092959891949750929550565b6000806040838503121561443057600080fd5b614439836142c3565b946020939093013593505050565b60408152600061445a604083018561401d565b8281036020840152614238818561401d565b600061010063ffffffff80845116855280602085015116602086015280604085015116604086015250606083015160608501526080830151608085015260a083015160a085015260c08301518160c08601526144ca82860182614101565b91505060e083015184820360e08601526142388282614101565b602081526000612b50602083018461446c565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156142b6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261455a8583516142f7565b94509285019290850190600101614520565b6000806000806040858703121561458257600080fd5b843567ffffffffffffffff8082111561459a57600080fd5b6145a68883890161407a565b909650945060208701359150808211156145bf57600080fd5b506145cc8782880161407a565b95989497509550505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b805182526000602082015160e0602085015261462660e085018261401d565b90506040830151848203604086015261463f828261401d565b915050606083015160048110614657576146576145d8565b6060850152608083015160028110614671576146716145d8565b8060808601525060a083015161469f60a086018273ffffffffffffffffffffffffffffffffffffffff169052565b5060c08301516146b360c086018215159052565b509392505050565b602081526000612b506020830184614607565b600080600080600080600060a0888a0312156146e957600080fd5b873567ffffffffffffffff8082111561470157600080fd5b61470d8b838c0161407a565b909950975060208a013591508082111561472657600080fd5b506147338a828b0161407a565b909650945061474690506040890161435a565b92506144016060890161435a565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156142b6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526147b7858351614607565b9450928501929085019060010161477d565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156142b6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261482c85835161446c565b945092850192908501906001016147f2565b803573ffffffffffffffffffffffffffffffffffffffff811681146142d757600080fd5b60006020828403121561487457600080fd5b612b508261483e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b63ffffffff8281168282160390808211156107ea576107ea6148ac565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614929576149296148ac565b5060010190565b600181811c9082168061494457607f821691505b60208210810361497d577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600063ffffffff80831681810361499c5761499c6148ac565b6001019392505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff618336030181126149da57600080fd5b9190910192915050565b600060a082360312156149f657600080fd5b6149fe613e68565b614a07836142c3565b8152602080840135818301526040840135604083015260608401356060830152608084013567ffffffffffffffff80821115614a4257600080fd5b9085019036601f830112614a5557600080fd5b813581811115614a6757614a67613e39565b8060051b9150614a78848301613e91565b8181529183018401918481019036841115614a9257600080fd5b938501935b83851015614ab057843582529385019390850190614a97565b608087015250939695505050505050565b6020808252825182820181905260009190848201906040850190845b81811015614af957835183529284019291840191600101614add565b50909695505050505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18336030181126149da57600080fd5b600060408236031215614b4b57600080fd5b6040516040810167ffffffffffffffff8282108183111715614b6f57614b6f613e39565b81604052614b7c8561483e565b83526020850135915080821115614b9257600080fd5b50614b9f36828601613f26565b60208301525092915050565b601f82111561068f576000816000526020600020601f850160051c81016020861015614bd45750805b601f850160051c820191505b8181101561261657828155600101614be0565b815167ffffffffffffffff811115614c0d57614c0d613e39565b614c2181614c1b8454614930565b84614bab565b602080601f831160018114614c745760008415614c3e5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555612616565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015614cc157888601518255948401946001909101908401614ca2565b5085821015614cfd57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b600060208284031215614d1f57600080fd5b815167ffffffffffffffff811115614d3657600080fd5b8201601f81018413614d4757600080fd5b8051614d55613f4582613ee0565b818152856020838501011115614d6a57600080fd5b614238826020830160208601613ff9565b8035600281106142d757600080fd5b600060a08236031215614d9c57600080fd5b614da4613e68565b823567ffffffffffffffff80821115614dbc57600080fd5b614dc836838701613f26565b83526020850135915080821115614dde57600080fd5b50614deb36828601613f26565b602083015250604083013560048110614e0357600080fd5b6040820152614e1460608401614d7b565b6060820152614e256080840161483e565b608082015292915050565b6000602080835260008454614e4481614930565b8060208701526040600180841660008114614e665760018114614ea057614ed0565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00851660408a0152604084151560051b8a01019550614ed0565b89600052602060002060005b85811015614ec75781548b8201860152908301908801614eac565b8a016040019650505b509398975050505050505050565b60ff8181168382160190811115610474576104746148ac565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112614f2c57600080fd5b83018035915067ffffffffffffffff821115614f4757600080fd5b60200191503681900382131561184857600080fd5b67ffffffffffffffff831115614f7457614f74613e39565b614f8883614f828354614930565b83614bab565b6000601f841160018114614fda5760008515614fa45750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355615070565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156150295786850135825560209485019460019092019101615009565b5086821015615064577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b81810381811115610474576104746148ac565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6080815284608082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8611156150f257600080fd5b8560051b808860a0850137820182810360a090810160208501526151189082018761401d565b91505063ffffffff8085166040840152808416606084015250969550505050505056fea164736f6c6343000818000a", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AccessForbidden\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityIsDeprecated\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"CapabilityRequiredByDON\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"DONDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONCapability\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONNode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedConfigurationContract\",\"type\":\"address\"}],\"name\":\"InvalidCapabilityConfigurationContractInterface\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"nodeCount\",\"type\":\"uint256\"}],\"name\":\"InvalidFaultTolerance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"name\":\"InvalidNodeCapabilities\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"encryptionPublicKey\",\"type\":\"bytes32\"}],\"name\":\"InvalidNodeEncryptionPublicKey\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeOperatorAdmin\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"InvalidNodeP2PId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"lengthOne\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lengthTwo\",\"type\":\"uint256\"}],\"name\":\"LengthMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"NodeAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"NodeDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"NodeDoesNotSupportCapability\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"}],\"name\":\"NodeOperatorDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"NodePartOfCapabilitiesDON\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"NodePartOfWorkflowDON\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityConfigured\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDeprecated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"}],\"name\":\"NodeAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"}],\"name\":\"NodeOperatorRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"NodeRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"}],\"name\":\"NodeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"labelledName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityType\",\"name\":\"capabilityType\",\"type\":\"uint8\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilitiesRegistry.Capability[]\",\"name\":\"capabilities\",\"type\":\"tuple[]\"}],\"name\":\"addCapabilities\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"acceptsWorkflows\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"}],\"name\":\"addDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilitiesRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"addNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"encryptionPublicKey\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilitiesRegistry.NodeParams[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"addNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"name\":\"deprecateCapabilities\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCapabilities\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"},{\"internalType\":\"string\",\"name\":\"labelledName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityType\",\"name\":\"capabilityType\",\"type\":\"uint8\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isDeprecated\",\"type\":\"bool\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"}],\"name\":\"getCapability\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"},{\"internalType\":\"string\",\"name\":\"labelledName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityType\",\"name\":\"capabilityType\",\"type\":\"uint8\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isDeprecated\",\"type\":\"bool\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityInfo\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"getCapabilityConfigs\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"getDON\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"id\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"acceptsWorkflows\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodeP2PIds\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"}],\"internalType\":\"structCapabilitiesRegistry.DONInfo\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDONs\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"id\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"acceptsWorkflows\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodeP2PIds\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"}],\"internalType\":\"structCapabilitiesRegistry.DONInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"labelledName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"}],\"name\":\"getHashedCapabilityId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"getNode\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"workflowDONId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"encryptionPublicKey\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256[]\",\"name\":\"capabilitiesDONIds\",\"type\":\"uint256[]\"}],\"internalType\":\"structCapabilitiesRegistry.NodeInfo\",\"name\":\"nodeInfo\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"}],\"name\":\"getNodeOperator\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilitiesRegistry.NodeOperator\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNodeOperators\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilitiesRegistry.NodeOperator[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNodes\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"workflowDONId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"encryptionPublicKey\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256[]\",\"name\":\"capabilitiesDONIds\",\"type\":\"uint256[]\"}],\"internalType\":\"structCapabilitiesRegistry.NodeInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"isCapabilityDeprecated\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"donIds\",\"type\":\"uint32[]\"}],\"name\":\"removeDONs\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint32[]\"}],\"name\":\"removeNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"removedNodeP2PIds\",\"type\":\"bytes32[]\"}],\"name\":\"removeNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"}],\"name\":\"updateDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint32[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilitiesRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"updateNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"encryptionPublicKey\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilitiesRegistry.NodeParams[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"updateNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6080604052600e80546001600160401b0319166401000000011790553480156200002857600080fd5b503380600081620000805760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000b357620000b381620000bc565b50505062000167565b336001600160a01b03821603620001165760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000077565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b61519f80620001776000396000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c806350c946fe116100ee57806386fa424611610097578063d8bc7b6811610071578063d8bc7b6814610400578063ddbe4f8214610413578063e29581aa14610428578063f2fde38b1461043d57600080fd5b806386fa4246146103a55780638da5cb5b146103b85780639cb7c5f4146103e057600080fd5b8063715f5295116100c8578063715f52951461037757806379ba50971461038a57806384f5ed8a1461039257600080fd5b806350c946fe1461032f5780635d83d9671461034f57806366acaa331461036257600080fd5b8063235374051161015b5780632c01a1e8116101355780632c01a1e8146102d5578063358039f4146102e8578063398f3773146102fb5780633f2a13c91461030e57600080fd5b8063235374051461028f578063275459f2146102af5780632a852933146102c257600080fd5b80631d05394c1161018c5780631d05394c14610245578063214502431461025a57806322bdbcbc1461026f57600080fd5b80630fe5800a146101b357806312570011146101d9578063181f5a77146101fc575b600080fd5b6101c66101c1366004613fd3565b610450565b6040519081526020015b60405180910390f35b6101ec6101e7366004614037565b610484565b60405190151581526020016101d0565b6102386040518060400160405280601a81526020017f4361706162696c6974696573526567697374727920312e302e3000000000000081525081565b6040516101d091906140be565b610258610253366004614116565b610491565b005b61026261069e565b6040516101d09190614298565b61028261027d366004614333565b6107fb565b6040516101d0919061438b565b6102a261029d366004614333565b6108e8565b6040516101d0919061439e565b6102586102bd366004614116565b61092c565b6102586102d03660046143d2565b610a03565b6102586102e3366004614116565b610ae3565b6102586102f6366004614116565b610d86565b610258610309366004614116565b6114fb565b61032161031c366004614474565b6116ba565b6040516101d092919061449e565b61034261033d366004614037565b6118a6565b6040516101d0919061453b565b61025861035d366004614116565b611993565b61036a611a88565b6040516101d0919061454e565b610258610385366004614116565b611c6b565b610258611d1d565b6102586103a0366004614116565b611e1a565b6102586103b33660046145c3565b612335565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101d0565b6103f36103ee366004614037565b612675565b6040516101d09190614712565b61025861040e366004614725565b6128b0565b61041b61297a565b6040516101d091906147ab565b610430612a6e565b6040516101d09190614820565b61025861044b3660046148b9565b612b7f565b6000828260405160200161046592919061449e565b6040516020818303038152906040528051906020012090505b92915050565b600061047e600583612b93565b610499612bae565b60005b818110156106995760008383838181106104b8576104b86148d4565b90506020020160208101906104cd9190614333565b63ffffffff8181166000908152600d60209081526040808320805464010000000081049095168085526001820190935290832094955093909290916a010000000000000000000090910460ff16905b61052583612c31565b8110156105c557811561057b57600c60006105408584612c3b565b8152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff1690556105bd565b6105bb8663ffffffff16600c600061059c8588612c3b90919063ffffffff16565b8152602001908152602001600020600501612c4790919063ffffffff16565b505b60010161051c565b508354640100000000900463ffffffff1660000361061c576040517f2b62be9b00000000000000000000000000000000000000000000000000000000815263ffffffff861660048201526024015b60405180910390fd5b63ffffffff85166000818152600d6020908152604080832080547fffffffffffffffffffffffffffffffffffffffffff0000000000000000000000169055519182527ff264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651910160405180910390a2505050505080600101905061049c565b505050565b600e54606090640100000000900463ffffffff1660006106bf600183614932565b63ffffffff1667ffffffffffffffff8111156106dd576106dd613e90565b60405190808252806020026020018201604052801561076457816020015b6040805160e081018252600080825260208083018290529282018190526060808301829052608083019190915260a0820181905260c082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816106fb5790505b509050600060015b8363ffffffff168163ffffffff1610156107d85763ffffffff8082166000908152600d602052604090205416156107d0576107a681612c53565b8383815181106107b8576107b86148d4565b6020026020010181905250816107cd9061494f565b91505b60010161076c565b506107e4600184614932565b63ffffffff1681146107f4578082525b5092915050565b60408051808201909152600081526060602082015263ffffffff82166000908152600b60209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff168352600181018054919284019161085f90614987565b80601f016020809104026020016040519081016040528092919081815260200182805461088b90614987565b80156108d85780601f106108ad576101008083540402835291602001916108d8565b820191906000526020600020905b8154815290600101906020018083116108bb57829003601f168201915b5050505050815250509050919050565b6040805160e0810182526000808252602082018190529181018290526060808201839052608082019290925260a0810182905260c081019190915261047e82612c53565b610934612bae565b60005b63ffffffff811682111561069957600083838363ffffffff1681811061095f5761095f6148d4565b90506020020160208101906109749190614333565b63ffffffff81166000908152600b6020526040812080547fffffffffffffffffffffffff00000000000000000000000000000000000000001681559192506109bf6001830182613e23565b505060405163ffffffff8216907fa59268ca81d40429e65ccea5385b59cf2d3fc6519371dee92f8eb1dae5107a7a90600090a2506109fc816149da565b9050610937565b610a0b612bae565b63ffffffff8088166000908152600d60205260408120805490926401000000009091041690819003610a71576040517f2b62be9b00000000000000000000000000000000000000000000000000000000815263ffffffff8a166004820152602401610613565b610ad8888888886040518060a001604052808f63ffffffff16815260200187610a99906149da565b63ffffffff811682528b15156020830152895460ff6a01000000000000000000009091048116151560408401528b166060909201919091529650612f1e565b505050505050505050565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015610d80576000848483818110610b1d57610b1d6148d4565b602090810292909201356000818152600c90935260409092206001810154929350919050610b7a576040517fd82f6adb00000000000000000000000000000000000000000000000000000000815260048101839052602401610613565b6000610b8882600501612c31565b1115610bdd57610b9b6005820184612c3b565b6040517f60a6d89800000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015260248101839052604401610613565b805468010000000000000000900463ffffffff1615610c455780546040517f60b9df730000000000000000000000000000000000000000000000000000000081526801000000000000000090910463ffffffff16600482015260248101839052604401610613565b83158015610c7f5750805463ffffffff166000908152600b602052604090205473ffffffffffffffffffffffffffffffffffffffff163314155b15610cb8576040517f9473075d000000000000000000000000000000000000000000000000000000008152336004820152602401610613565b6001810154610cc990600790612c47565b506002810154610cdb90600990612c47565b506000828152600c6020526040812080547fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001681556001810182905560028101829055600381018290559060058201818181610d378282613e5d565b5050505050507f5254e609a97bab37b7cc79fe128f85c097bd6015c6e1624ae0ba392eb975320582604051610d6e91815260200190565b60405180910390a15050600101610b01565b50505050565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015610d80576000848483818110610dc057610dc06148d4565b9050602002810190610dd291906149fd565b610ddb90614a3b565b6040808201516000908152600c6020908152828220805463ffffffff168352600b82528383208451808601909552805473ffffffffffffffffffffffffffffffffffffffff1685526001810180549697509195939493909284019190610e4090614987565b80601f0160208091040260200160405190810160405280929190818152602001828054610e6c90614987565b8015610eb95780601f10610e8e57610100808354040283529160200191610eb9565b820191906000526020600020905b815481529060010190602001808311610e9c57829003601f168201915b505050919092525050506001830154909150610f095782604001516040517fd82f6adb00000000000000000000000000000000000000000000000000000000815260040161061391815260200190565b84158015610f2e5750805173ffffffffffffffffffffffffffffffffffffffff163314155b15610f67576040517f9473075d000000000000000000000000000000000000000000000000000000008152336004820152602401610613565b6020830151610fa2576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600182015460208401518114611023576020840151610fc390600790612b93565b15610ffa576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020840151600184015561100f600782612c47565b50602084015161102190600790613735565b505b60608401516110665783606001516040517f37d8976500000000000000000000000000000000000000000000000000000000815260040161061391815260200190565b608084015180516000036110a857806040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016106139190614b18565b835460009085906004906110c990640100000000900463ffffffff166149da565b91906101000a81548163ffffffff021916908363ffffffff1602179055905060005b82518110156111ae57611121838281518110611109576111096148d4565b60200260200101516003612b9390919063ffffffff16565b61115957826040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016106139190614b18565b6111a583828151811061116e5761116e6148d4565b60200260200101518760040160008563ffffffff1663ffffffff16815260200190815260200160002061373590919063ffffffff16565b506001016110eb565b50845468010000000000000000900463ffffffff16801561130f5763ffffffff8082166000908152600d60209081526040808320805464010000000090049094168352600190930181528282206002018054845181840281018401909552808552929392909183018282801561124357602002820191906000526020600020905b81548152602001906001019080831161122f575b5050505050905060005b815181101561130c576112a282828151811061126b5761126b6148d4565b60200260200101518960040160008763ffffffff1663ffffffff168152602001908152602001600020612b9390919063ffffffff16565b611304578181815181106112b8576112b86148d4565b6020026020010151836040517f03dcd86200000000000000000000000000000000000000000000000000000000815260040161061392919091825263ffffffff16602082015260400190565b60010161124d565b50505b600061131d87600501613741565b905060005b81518163ffffffff161015611463576000828263ffffffff168151811061134b5761134b6148d4565b60209081029190910181015163ffffffff8082166000908152600d845260408082208054640100000000900490931682526001909201845281812060020180548351818702810187019094528084529395509093919290918301828280156113d257602002820191906000526020600020905b8154815260200190600101908083116113be575b5050505050905060005b815181101561144f576114318282815181106113fa576113fa6148d4565b60200260200101518c60040160008a63ffffffff1663ffffffff168152602001908152602001600020612b9390919063ffffffff16565b611447578181815181106112b8576112b86148d4565b6001016113dc565b5050508061145c906149da565b9050611322565b50875187547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff90911690811788556040808a015160028a0181905560608b015160038b01556020808c01518351928352908201527f4b5b465e22eea0c3d40c30e936643245b80d19b2dcf75788c0699fe8d8db645b910160405180910390a25050505050505050806001019050610da4565b611503612bae565b60005b81811015610699576000838383818110611522576115226148d4565b90506020028101906115349190614b5c565b61153d90614b90565b805190915073ffffffffffffffffffffffffffffffffffffffff1661158e576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600e54604080518082018252835173ffffffffffffffffffffffffffffffffffffffff908116825260208086015181840190815263ffffffff9095166000818152600b909252939020825181547fffffffffffffffffffffffff0000000000000000000000000000000000000000169216919091178155925191929091600182019061161a9082614c4a565b5050600e80549091506000906116359063ffffffff166149da565b91906101000a81548163ffffffff021916908363ffffffff160217905550816000015173ffffffffffffffffffffffffffffffffffffffff168163ffffffff167f78e94ca80be2c30abc061b99e7eb8583b1254781734b1e3ce339abb57da2fe8e84602001516040516116a891906140be565b60405180910390a35050600101611506565b63ffffffff8083166000908152600d6020908152604080832080546401000000009004909416808452600190940182528083208584526003019091528120805460609384939092909161170c90614987565b80601f016020809104026020016040519081016040528092919081815260200182805461173890614987565b80156117855780601f1061175a57610100808354040283529160200191611785565b820191906000526020600020905b81548152906001019060200180831161176857829003601f168201915b5050506000888152600260208190526040909120015492935060609262010000900473ffffffffffffffffffffffffffffffffffffffff16159150611898905057600086815260026020819052604091829020015490517f8318ed5d00000000000000000000000000000000000000000000000000000000815263ffffffff891660048201526201000090910473ffffffffffffffffffffffffffffffffffffffff1690638318ed5d90602401600060405180830381865afa15801561184f573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526118959190810190614d64565b90505b9093509150505b9250929050565b604080516101008101825260008082526020820181905291810182905260608082018390526080820183905260a082019290925260c0810182905260e081019190915260408051610100810182526000848152600c6020908152838220805463ffffffff8082168652640100000000820481168487018190526801000000000000000090920416858701526001820154606086015260028201546080860152600382015460a0860152835260040190529190912060c082019061196890613741565b815260200161198b600c6000868152602001908152602001600020600501613741565b905292915050565b61199b612bae565b60005b818110156106995760008383838181106119ba576119ba6148d4565b9050602002013590506119d7816003612b9390919063ffffffff16565b611a10576040517fe181733f00000000000000000000000000000000000000000000000000000000815260048101829052602401610613565b611a1b600582613735565b611a54576040517ff7d7a29400000000000000000000000000000000000000000000000000000000815260048101829052602401610613565b60405181907fdcea1b78b6ddc31592a94607d537543fcaafda6cc52d6d5cc7bbfca1422baf2190600090a25060010161199e565b600e5460609063ffffffff166000611aa1600183614932565b63ffffffff1667ffffffffffffffff811115611abf57611abf613e90565b604051908082528060200260200182016040528015611b0557816020015b604080518082019091526000815260606020820152815260200190600190039081611add5790505b509050600060015b8363ffffffff168163ffffffff161015611c555763ffffffff81166000908152600b602052604090205473ffffffffffffffffffffffffffffffffffffffff1615611c4d5763ffffffff81166000908152600b60209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff1683526001810180549192840191611ba190614987565b80601f0160208091040260200160405190810160405280929190818152602001828054611bcd90614987565b8015611c1a5780601f10611bef57610100808354040283529160200191611c1a565b820191906000526020600020905b815481529060010190602001808311611bfd57829003601f168201915b505050505081525050838381518110611c3557611c356148d4565b602002602001018190525081611c4a9061494f565b91505b600101611b0d565b50600e546107e49060019063ffffffff16614932565b611c73612bae565b60005b81811015610699576000838383818110611c9257611c926148d4565b9050602002810190611ca491906149fd565b611cad90614de1565b90506000611cc382600001518360200151610450565b9050611cd0600382613735565b611d09576040517febf5255100000000000000000000000000000000000000000000000000000000815260048101829052602401610613565b611d13818361374e565b5050600101611c76565b60015473ffffffffffffffffffffffffffffffffffffffff163314611d9e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610613565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015610d80576000848483818110611e5457611e546148d4565b9050602002810190611e6691906149fd565b611e6f90614a3b565b805163ffffffff166000908152600b602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff168252600181018054959650939491939092840191611ec590614987565b80601f0160208091040260200160405190810160405280929190818152602001828054611ef190614987565b8015611f3e5780601f10611f1357610100808354040283529160200191611f3e565b820191906000526020600020905b815481529060010190602001808311611f2157829003601f168201915b50505091909252505081519192505073ffffffffffffffffffffffffffffffffffffffff16611fa45781516040517fadd9ae1e00000000000000000000000000000000000000000000000000000000815263ffffffff9091166004820152602401610613565b83158015611fc95750805173ffffffffffffffffffffffffffffffffffffffff163314155b15612002576040517f9473075d000000000000000000000000000000000000000000000000000000008152336004820152602401610613565b6040808301516000908152600c602052206001810154156120575782604001516040517f5461848300000000000000000000000000000000000000000000000000000000815260040161061391815260200190565b604083015161209a5782604001516040517f64e2ee9200000000000000000000000000000000000000000000000000000000815260040161061391815260200190565b602083015115806120b7575060208301516120b790600790612b93565b156120ee576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608301516121315782606001516040517f37d8976500000000000000000000000000000000000000000000000000000000815260040161061391815260200190565b6080830151805160000361217357806040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016106139190614b18565b8154829060049061219190640100000000900463ffffffff166149da565b82546101009290920a63ffffffff818102199093169183160217909155825464010000000090041660005b8251811015612267576121da838281518110611109576111096148d4565b61221257826040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016106139190614b18565b61225e838281518110612227576122276148d4565b60200260200101518560040160008563ffffffff1663ffffffff16815260200190815260200160002061373590919063ffffffff16565b506001016121bc565b5060608501516003840155845183547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff918216178455604086015160028501556020860151600185018190556122c7916007919061373516565b5060408501516122d990600990613735565b50845160408087015160208089015183519283529082015263ffffffff909216917f74becb12a5e8fd0e98077d02dfba8f647c9670c9df177e42c2418cf17a636f05910160405180910390a25050505050806001019050611e38565b828114612378576040517fab8b67c60000000000000000000000000000000000000000000000000000000081526004810184905260248101829052604401610613565b6000805473ffffffffffffffffffffffffffffffffffffffff16905b8481101561266d5760008686838181106123b0576123b06148d4565b90506020020160208101906123c59190614333565b63ffffffff81166000908152600b6020526040902080549192509073ffffffffffffffffffffffffffffffffffffffff16612434576040517fadd9ae1e00000000000000000000000000000000000000000000000000000000815263ffffffff83166004820152602401610613565b6000868685818110612448576124486148d4565b905060200281019061245a9190614b5c565b61246390614b90565b805190915073ffffffffffffffffffffffffffffffffffffffff166124b4576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815473ffffffffffffffffffffffffffffffffffffffff1633148015906124f157503373ffffffffffffffffffffffffffffffffffffffff861614155b1561252a576040517f9473075d000000000000000000000000000000000000000000000000000000008152336004820152602401610613565b8051825473ffffffffffffffffffffffffffffffffffffffff90811691161415806125a6575060208082015160405161256392016140be565b604051602081830303815290604052805190602001208260010160405160200161258d9190614e87565b6040516020818303038152906040528051906020012014155b1561265f57805182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178255602081015160018301906126009082614c4a565b50806000015173ffffffffffffffffffffffffffffffffffffffff168363ffffffff167f86f41145bde5dd7f523305452e4aad3685508c181432ec733d5f345009358a28836020015160405161265691906140be565b60405180910390a35b505050806001019050612394565b505050505050565b6126b66040805160e0810182526000808252606060208301819052928201839052909182019081526020016000815260006020820181905260409091015290565b6040805160e081018252838152600084815260026020908152929020805491928301916126e290614987565b80601f016020809104026020016040519081016040528092919081815260200182805461270e90614987565b801561275b5780601f106127305761010080835404028352916020019161275b565b820191906000526020600020905b81548152906001019060200180831161273e57829003601f168201915b5050505050815260200160026000858152602001908152602001600020600101805461278690614987565b80601f01602080910402602001604051908101604052809291908181526020018280546127b290614987565b80156127ff5780601f106127d4576101008083540402835291602001916127ff565b820191906000526020600020905b8154815290600101906020018083116127e257829003601f168201915b50505091835250506000848152600260208181526040909220015491019060ff1660038111156128315761283161462f565b815260008481526002602081815260409092200154910190610100900460ff1660018111156128625761286261462f565b81526000848152600260208181526040928390209091015462010000900473ffffffffffffffffffffffffffffffffffffffff1690830152016128a6600585612b93565b1515905292915050565b6128b8612bae565b600e805460009164010000000090910463ffffffff169060046128da836149da565b82546101009290920a63ffffffff81810219909316918316021790915581166000818152600d602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001684179055815160a08101835292835260019083015286151590820152841515606082015260ff84166080820152909150612970908990899089908990612f1e565b5050505050505050565b606060006129886003613741565b90506000815167ffffffffffffffff8111156129a6576129a6613e90565b604051908082528060200260200182016040528015612a1857816020015b612a056040805160e0810182526000808252606060208301819052928201839052909182019081526020016000815260006020820181905260409091015290565b8152602001906001900390816129c45790505b50905060005b82518110156107f457612a49838281518110612a3c57612a3c6148d4565b6020026020010151612675565b828281518110612a5b57612a5b6148d4565b6020908102919091010152600101612a1e565b60606000612a7c6009613741565b90506000815167ffffffffffffffff811115612a9a57612a9a613e90565b604051908082528060200260200182016040528015612b2957816020015b60408051610100810182526000808252602080830182905292820181905260608083018290526080830182905260a083019190915260c0820181905260e082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909201910181612ab85790505b50905060005b82518110156107f457612b5a838281518110612b4d57612b4d6148d4565b60200260200101516118a6565b828281518110612b6c57612b6c6148d4565b6020908102919091010152600101612b2f565b612b87612bae565b612b9081613936565b50565b600081815260018301602052604081205415155b9392505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314612c2f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610613565b565b600061047e825490565b6000612ba78383613a2b565b6000612ba78383613a55565b6040805160e0810182526000808252602080830182905282840182905260608084018390526080840183905260a0840181905260c084015263ffffffff8581168352600d8252848320805464010000000090049091168084526001909101825284832060028101805487518186028101860190985280885295969295919493909190830182828015612d0457602002820191906000526020600020905b815481526020019060010190808311612cf0575b505050505090506000815167ffffffffffffffff811115612d2757612d27613e90565b604051908082528060200260200182016040528015612d6d57816020015b604080518082019091526000815260606020820152815260200190600190039081612d455790505b50905060005b8151811015612e85576040518060400160405280848381518110612d9957612d996148d4565b60200260200101518152602001856003016000868581518110612dbe57612dbe6148d4565b602002602001015181526020019081526020016000208054612ddf90614987565b80601f0160208091040260200160405190810160405280929190818152602001828054612e0b90614987565b8015612e585780601f10612e2d57610100808354040283529160200191612e58565b820191906000526020600020905b815481529060010190602001808311612e3b57829003601f168201915b5050505050815250828281518110612e7257612e726148d4565b6020908102919091010152600101612d73565b506040805160e08101825263ffffffff8089166000818152600d6020818152868320548086168752948b168187015260ff680100000000000000008604811697870197909752690100000000000000000085048716151560608701529290915290526a010000000000000000000090049091161515608082015260a08101612f0c85613741565b81526020019190915295945050505050565b805163ffffffff9081166000908152600d602090815260408083208286015190941683526001909301905220608082015160ff161580612f70575060808201518590612f6b906001614f35565b60ff16115b15612fb95760808201516040517f25b4d61800000000000000000000000000000000000000000000000000000000815260ff909116600482015260248101869052604401610613565b6001826020015163ffffffff1611156130a157815163ffffffff166000908152600d602090815260408220908401516001918201918391612ffa9190614932565b63ffffffff1663ffffffff168152602001908152602001600020905060005b61302282612c31565b81101561309e57613051846000015163ffffffff16600c600061059c8587600001612c3b90919063ffffffff16565b50600c60006130608484612c3b565b8152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff169055600101613019565b50505b60005b858110156132db576130d18787838181106130c1576130c16148d4565b8592602090910201359050613735565b6131325782518787838181106130e9576130e96148d4565b6040517f636e405700000000000000000000000000000000000000000000000000000000815263ffffffff90941660048501526020029190910135602483015250604401610613565b82606001511561328957825163ffffffff16600c600089898581811061315a5761315a6148d4565b602090810292909201358352508101919091526040016000205468010000000000000000900463ffffffff16148015906131d45750600c60008888848181106131a5576131a56148d4565b602090810292909201358352508101919091526040016000205468010000000000000000900463ffffffff1615155b156132365782518787838181106131ed576131ed6148d4565b6040517f60b9df7300000000000000000000000000000000000000000000000000000000815263ffffffff90941660048501526020029190910135602483015250604401610613565b8251600c600089898581811061324e5761324e6148d4565b90506020020135815260200190815260200160002060000160086101000a81548163ffffffff021916908363ffffffff1602179055506132d3565b82516132d19063ffffffff16600c60008a8a868181106132ab576132ab6148d4565b90506020020135815260200190815260200160002060050161373590919063ffffffff16565b505b6001016130a4565b5060005b838110156136e957368585838181106132fa576132fa6148d4565b905060200281019061330c9190614b5c565b905061331a60038235612b93565b613353576040517fe181733f00000000000000000000000000000000000000000000000000000000815281356004820152602401610613565b61335f60058235612b93565b15613399576040517ff7d7a29400000000000000000000000000000000000000000000000000000000815281356004820152602401610613565b80356000908152600384016020526040812080546133b690614987565b905011156134025783516040517f3927d08000000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015281356024820152604401610613565b60005b8781101561350c576134a98235600c60008c8c86818110613428576134286148d4565b9050602002013581526020019081526020016000206004016000600c60008e8e88818110613458576134586148d4565b90506020020135815260200190815260200160002060000160049054906101000a900463ffffffff1663ffffffff1663ffffffff168152602001908152602001600020612b9390919063ffffffff16565b613504578888828181106134bf576134bf6148d4565b6040517fa7e792500000000000000000000000000000000000000000000000000000000081526020909102929092013560048301525082356024820152604401610613565b600101613405565b506002830180546001810182556000918252602091829020833591015561353590820182614f4e565b82356000908152600386016020526040902091613553919083614fb3565b50604080850151855163ffffffff9081166000908152600d602090815284822080549415156901000000000000000000027fffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffff90951694909417909355606088015188518316825284822080549115156a0100000000000000000000027fffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffff9092169190911790556080880151885183168252848220805460ff9290921668010000000000000000027fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff909216919091179055828801805189518416835294909120805494909216640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff909416939093179055855191516136e092918435908c908c906136a690880188614f4e565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613b4892505050565b506001016132df565b50815160208084015160405163ffffffff91821681529216917ff264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651910160405180910390a2505050505050565b6000612ba78383613c29565b60606000612ba783613c78565b608081015173ffffffffffffffffffffffffffffffffffffffff16156137f05761379c81608001517f78bea72100000000000000000000000000000000000000000000000000000000613cd4565b6137f05760808101516040517fabb5e3fd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610613565b60008281526002602052604090208151829190819061380f9082614c4a565b50602082015160018201906138249082614c4a565b5060408201516002820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660018360038111156138665761386661462f565b021790555060608201516002820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101008360018111156138ad576138ad61462f565b0217905550608091909101516002909101805473ffffffffffffffffffffffffffffffffffffffff90921662010000027fffffffffffffffffffff0000000000000000000000000000000000000000ffff90921691909117905560405182907f04f0a9bcf3f3a3b42a4d7ca081119755f82ebe43e0d30c8f7292c4fe0dc4a2ae90600090a25050565b3373ffffffffffffffffffffffffffffffffffffffff8216036139b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610613565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000826000018281548110613a4257613a426148d4565b9060005260206000200154905092915050565b60008181526001830160205260408120548015613b3e576000613a796001836150ce565b8554909150600090613a8d906001906150ce565b9050818114613af2576000866000018281548110613aad57613aad6148d4565b9060005260206000200154905080876000018481548110613ad057613ad06148d4565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080613b0357613b036150e1565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061047e565b600091505061047e565b6000848152600260208190526040909120015462010000900473ffffffffffffffffffffffffffffffffffffffff161561266d57600084815260026020819052604091829020015490517ffba64a7c0000000000000000000000000000000000000000000000000000000081526201000090910473ffffffffffffffffffffffffffffffffffffffff169063fba64a7c90613bef908690869086908b908d90600401615110565b600060405180830381600087803b158015613c0957600080fd5b505af1158015613c1d573d6000803e3d6000fd5b50505050505050505050565b6000818152600183016020526040812054613c705750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561047e565b50600061047e565b606081600001805480602002602001604051908101604052809291908181526020018280548015613cc857602002820191906000526020600020905b815481526020019060010190808311613cb4575b50505050509050919050565b6000613cdf83613cf0565b8015612ba75750612ba78383613d54565b6000613d1c827f01ffc9a700000000000000000000000000000000000000000000000000000000613d54565b801561047e5750613d4d827fffffffff00000000000000000000000000000000000000000000000000000000613d54565b1592915050565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d91506000519050828015613e0c575060208210155b8015613e185750600081115b979650505050505050565b508054613e2f90614987565b6000825580601f10613e3f575050565b601f016020900490600052602060002090810190612b909190613e77565b5080546000825590600052602060002090810190612b9091905b5b80821115613e8c5760008155600101613e78565b5090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160a0810167ffffffffffffffff81118282101715613ee257613ee2613e90565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613f2f57613f2f613e90565b604052919050565b600067ffffffffffffffff821115613f5157613f51613e90565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f830112613f8e57600080fd5b8135613fa1613f9c82613f37565b613ee8565b818152846020838601011115613fb657600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215613fe657600080fd5b823567ffffffffffffffff80821115613ffe57600080fd5b61400a86838701613f7d565b9350602085013591508082111561402057600080fd5b5061402d85828601613f7d565b9150509250929050565b60006020828403121561404957600080fd5b5035919050565b60005b8381101561406b578181015183820152602001614053565b50506000910152565b6000815180845261408c816020860160208601614050565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000612ba76020830184614074565b60008083601f8401126140e357600080fd5b50813567ffffffffffffffff8111156140fb57600080fd5b6020830191508360208260051b850101111561189f57600080fd5b6000806020838503121561412957600080fd5b823567ffffffffffffffff81111561414057600080fd5b61414c858286016140d1565b90969095509350505050565b60008151808452602080850194506020840160005b838110156141895781518752958201959082019060010161416d565b509495945050505050565b600082825180855260208086019550808260051b84010181860160005b84811015614211578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001895281518051845284015160408585018190526141fd81860183614074565b9a86019a94505050908301906001016141b1565b5090979650505050505050565b600063ffffffff8083511684528060208401511660208501525060ff604083015116604084015260608201511515606084015260808201511515608084015260a082015160e060a085015261427660e0850182614158565b905060c083015184820360c086015261428f8282614194565b95945050505050565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b8281101561430d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526142fb85835161421e565b945092850192908501906001016142c1565b5092979650505050505050565b803563ffffffff8116811461432e57600080fd5b919050565b60006020828403121561434557600080fd5b612ba78261431a565b73ffffffffffffffffffffffffffffffffffffffff815116825260006020820151604060208501526143836040850182614074565b949350505050565b602081526000612ba7602083018461434e565b602081526000612ba7602083018461421e565b8035801515811461432e57600080fd5b803560ff8116811461432e57600080fd5b600080600080600080600060a0888a0312156143ed57600080fd5b6143f68861431a565b9650602088013567ffffffffffffffff8082111561441357600080fd5b61441f8b838c016140d1565b909850965060408a013591508082111561443857600080fd5b506144458a828b016140d1565b90955093506144589050606089016143b1565b9150614466608089016143c1565b905092959891949750929550565b6000806040838503121561448757600080fd5b6144908361431a565b946020939093013593505050565b6040815260006144b16040830185614074565b828103602084015261428f8185614074565b600061010063ffffffff80845116855280602085015116602086015280604085015116604086015250606083015160608501526080830151608085015260a083015160a085015260c08301518160c086015261452182860182614158565b91505060e083015184820360e086015261428f8282614158565b602081526000612ba760208301846144c3565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b8281101561430d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526145b185835161434e565b94509285019290850190600101614577565b600080600080604085870312156145d957600080fd5b843567ffffffffffffffff808211156145f157600080fd5b6145fd888389016140d1565b9096509450602087013591508082111561461657600080fd5b50614623878288016140d1565b95989497509550505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b805182526000602082015160e0602085015261467d60e0850182614074565b9050604083015184820360408601526146968282614074565b9150506060830151600481106146ae576146ae61462f565b60608501526080830151600281106146c8576146c861462f565b8060808601525060a08301516146f660a086018273ffffffffffffffffffffffffffffffffffffffff169052565b5060c083015161470a60c086018215159052565b509392505050565b602081526000612ba7602083018461465e565b600080600080600080600060a0888a03121561474057600080fd5b873567ffffffffffffffff8082111561475857600080fd5b6147648b838c016140d1565b909950975060208a013591508082111561477d57600080fd5b5061478a8a828b016140d1565b909650945061479d9050604089016143b1565b9250614458606089016143b1565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b8281101561430d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261480e85835161465e565b945092850192908501906001016147d4565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b8281101561430d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526148838583516144c3565b94509285019290850190600101614849565b803573ffffffffffffffffffffffffffffffffffffffff8116811461432e57600080fd5b6000602082840312156148cb57600080fd5b612ba782614895565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b63ffffffff8281168282160390808211156107f4576107f4614903565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361498057614980614903565b5060010190565b600181811c9082168061499b57607f821691505b6020821081036149d4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600063ffffffff8083168181036149f3576149f3614903565b6001019392505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61833603018112614a3157600080fd5b9190910192915050565b600060a08236031215614a4d57600080fd5b614a55613ebf565b614a5e8361431a565b8152602080840135818301526040840135604083015260608401356060830152608084013567ffffffffffffffff80821115614a9957600080fd5b9085019036601f830112614aac57600080fd5b813581811115614abe57614abe613e90565b8060051b9150614acf848301613ee8565b8181529183018401918481019036841115614ae957600080fd5b938501935b83851015614b0757843582529385019390850190614aee565b608087015250939695505050505050565b6020808252825182820181905260009190848201906040850190845b81811015614b5057835183529284019291840191600101614b34565b50909695505050505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112614a3157600080fd5b600060408236031215614ba257600080fd5b6040516040810167ffffffffffffffff8282108183111715614bc657614bc6613e90565b81604052614bd385614895565b83526020850135915080821115614be957600080fd5b50614bf636828601613f7d565b60208301525092915050565b601f821115610699576000816000526020600020601f850160051c81016020861015614c2b5750805b601f850160051c820191505b8181101561266d57828155600101614c37565b815167ffffffffffffffff811115614c6457614c64613e90565b614c7881614c728454614987565b84614c02565b602080601f831160018114614ccb5760008415614c955750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b17855561266d565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015614d1857888601518255948401946001909101908401614cf9565b5085821015614d5457878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b600060208284031215614d7657600080fd5b815167ffffffffffffffff811115614d8d57600080fd5b8201601f81018413614d9e57600080fd5b8051614dac613f9c82613f37565b818152856020838501011115614dc157600080fd5b61428f826020830160208601614050565b80356002811061432e57600080fd5b600060a08236031215614df357600080fd5b614dfb613ebf565b823567ffffffffffffffff80821115614e1357600080fd5b614e1f36838701613f7d565b83526020850135915080821115614e3557600080fd5b50614e4236828601613f7d565b602083015250604083013560048110614e5a57600080fd5b6040820152614e6b60608401614dd2565b6060820152614e7c60808401614895565b608082015292915050565b6000602080835260008454614e9b81614987565b8060208701526040600180841660008114614ebd5760018114614ef757614f27565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00851660408a0152604084151560051b8a01019550614f27565b89600052602060002060005b85811015614f1e5781548b8201860152908301908801614f03565b8a016040019650505b509398975050505050505050565b60ff818116838216019081111561047e5761047e614903565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112614f8357600080fd5b83018035915067ffffffffffffffff821115614f9e57600080fd5b60200191503681900382131561189f57600080fd5b67ffffffffffffffff831115614fcb57614fcb613e90565b614fdf83614fd98354614987565b83614c02565b6000601f8411600181146150315760008515614ffb5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b1783556150c7565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156150805786850135825560209485019460019092019101615060565b50868210156150bb577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b8181038181111561047e5761047e614903565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6080815284608082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86111561514957600080fd5b8560051b808860a0850137820182810360a0908101602085015261516f90820187614074565b91505063ffffffff8085166040840152808416606084015250969550505050505056fea164736f6c6343000818000a", } var CapabilitiesRegistryABI = CapabilitiesRegistryMetaData.ABI diff --git a/core/gethwrappers/keystone/generated/ocr3_capability/ocr3_capability.go b/core/gethwrappers/keystone/generated/ocr3_capability/ocr3_capability.go index 21aeb4975c9..a83278f0e42 100644 --- a/core/gethwrappers/keystone/generated/ocr3_capability/ocr3_capability.go +++ b/core/gethwrappers/keystone/generated/ocr3_capability/ocr3_capability.go @@ -31,8 +31,8 @@ var ( ) var OCR3CapabilityMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReportingUnsupported\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"previousConfigBlockNumber\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"configCount\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes[]\",\"name\":\"signers\",\"type\":\"bytes[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"onchainConfig\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"name\":\"Transmitted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDetails\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDigestAndEpoch\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"scanLogs\",\"type\":\"bool\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"_signers\",\"type\":\"bytes[]\"},{\"internalType\":\"address[]\",\"name\":\"_transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"_f\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"_onchainConfig\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"_offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"_offchainConfig\",\"type\":\"bytes\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"transmit\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", - Bin: "0x608060405234801561001057600080fd5b5033806000816100675760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615610097576100978161009f565b505050610148565b336001600160a01b038216036100f75760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161005e565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b611266806101576000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c80638da5cb5b1161005b5780638da5cb5b1461015f578063afcb95d714610187578063b1dc65a4146101a7578063f2fde38b146101ba57600080fd5b8063181f5a771461008d57806379ba5097146100d55780637f3c87d3146100df57806381ff7048146100f2575b600080fd5b604080518082018252600e81527f4b657973746f6e6520312e302e30000000000000000000000000000000000000602082015290516100cc9190610b02565b60405180910390f35b6100dd6101cd565b005b6100dd6100ed366004610c70565b6102cf565b61013c60015460025463ffffffff74010000000000000000000000000000000000000000830481169378010000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff9485168152939092166020840152908201526060016100cc565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100cc565b6040805160018152600060208201819052918101919091526060016100cc565b6100dd6101b5366004610d4c565b61082f565b6100dd6101c8366004610e55565b610861565b60015473ffffffffffffffffffffffffffffffffffffffff163314610253576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b868560ff8616601f831115610340576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f746f6f206d616e79207369676e65727300000000000000000000000000000000604482015260640161024a565b806000036103aa576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f66206d75737420626520706f7369746976650000000000000000000000000000604482015260640161024a565b818314610438576040517f89a61989000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f6f7261636c6520616464726573736573206f7574206f6620726567697374726160448201527f74696f6e00000000000000000000000000000000000000000000000000000000606482015260840161024a565b610443816003610e9f565b83116104ab576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f6661756c74792d6f7261636c65206620746f6f20686967680000000000000000604482015260640161024a565b6104b3610875565b60005b8a81101561069d5760008a8a838181106104d2576104d2610ebc565b90506020020160208101906104e79190610e55565b73ffffffffffffffffffffffffffffffffffffffff1603610564576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f7472616e736d6974746572206d757374206e6f7420626520656d707479000000604482015260640161024a565b3660008d8d8481811061057957610579610ebc565b905060200281019061058b9190610eeb565b90925090506000815b8061ffff168261ffff16101561068d57600084848461ffff168181106105bc576105bc610ebc565b919091013560f81c915060009050600886866105d9876002610f50565b61ffff168181106105ec576105ec610ebc565b919091013560f81c90911b90508686610606876001610f50565b61ffff1681811061061957610619610ebc565b61062a9392013560f81c9050610f50565b9050366000878761063c886003610f50565b61ffff16908561064d8a6003610f50565b6106579190610f50565b61ffff169261066893929190610f72565b9092509050610678836003610f50565b6106829087610f50565b955050505050610594565b5050600190920191506104b69050565b50600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff8916179055600180547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff8116780100000000000000000000000000000000000000000000000063ffffffff438116820292909217808555920481169291829160149161074c91849174010000000000000000000000000000000000000000900416610f9c565b92506101000a81548163ffffffff021916908363ffffffff1602179055506107954630600160149054906101000a900463ffffffff1663ffffffff168f8f8f8f8f8f8f8f6108f8565b6002600001819055508b8b9050600260010160016101000a81548160ff021916908360ff1602179055507f36257c6e8d535293ad661e377c0baac536289be6707b8a488ac175ddaa4055c881600260000154600160149054906101000a900463ffffffff168f8f8f8f8f8f8f8f6040516108199b9a99989796959493929190611128565b60405180910390a1505050505050505050505050565b6040517f0750181900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610869610875565b610872816109a9565b50565b60005473ffffffffffffffffffffffffffffffffffffffff1633146108f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640161024a565b565b6000808c8c8c8c8c8c8c8c8c8c8c6040516020016109209b9a999897969594939291906111c2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e0e000000000000000000000000000000000000000000000000000000000000179150509b9a5050505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603610a28576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161024a565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000815180845260005b81811015610ac457602081850181015186830182015201610aa8565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000610b156020830184610a9e565b9392505050565b60008083601f840112610b2e57600080fd5b50813567ffffffffffffffff811115610b4657600080fd5b6020830191508360208260051b8501011115610b6157600080fd5b9250929050565b803560ff81168114610b7957600080fd5b919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f830112610bbe57600080fd5b813567ffffffffffffffff80821115610bd957610bd9610b7e565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715610c1f57610c1f610b7e565b81604052838152866020858801011115610c3857600080fd5b836020870160208301376000602085830101528094505050505092915050565b803567ffffffffffffffff81168114610b7957600080fd5b60008060008060008060008060c0898b031215610c8c57600080fd5b883567ffffffffffffffff80821115610ca457600080fd5b610cb08c838d01610b1c565b909a50985060208b0135915080821115610cc957600080fd5b610cd58c838d01610b1c565b9098509650869150610ce960408c01610b68565b955060608b0135915080821115610cff57600080fd5b610d0b8c838d01610bad565b9450610d1960808c01610c58565b935060a08b0135915080821115610d2f57600080fd5b50610d3c8b828c01610bad565b9150509295985092959890939650565b60008060008060008060008060e0898b031215610d6857600080fd5b606089018a811115610d7957600080fd5b8998503567ffffffffffffffff80821115610d9357600080fd5b818b0191508b601f830112610da757600080fd5b813581811115610db657600080fd5b8c6020828501011115610dc857600080fd5b6020830199508098505060808b0135915080821115610de657600080fd5b610df28c838d01610b1c565b909750955060a08b0135915080821115610e0b57600080fd5b50610e188b828c01610b1c565b999c989b50969995989497949560c00135949350505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610b7957600080fd5b600060208284031215610e6757600080fd5b610b1582610e31565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082028115828204841417610eb657610eb6610e70565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610f2057600080fd5b83018035915067ffffffffffffffff821115610f3b57600080fd5b602001915036819003821315610b6157600080fd5b61ffff818116838216019080821115610f6b57610f6b610e70565b5092915050565b60008085851115610f8257600080fd5b83861115610f8f57600080fd5b5050820193919092039150565b63ffffffff818116838216019080821115610f6b57610f6b610e70565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b6000838385526020808601955060208560051b8301018460005b878110156110c7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe085840301895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe188360301811261107d57600080fd5b8701848101903567ffffffffffffffff81111561109957600080fd5b8036038213156110a857600080fd5b6110b3858284610fb9565b9a86019a945050509083019060010161101c565b5090979650505050505050565b8183526000602080850194508260005b8581101561111d5773ffffffffffffffffffffffffffffffffffffffff61110a83610e31565b16875295820195908201906001016110e4565b509495945050505050565b600061012063ffffffff808f1684528d6020850152808d166040850152508060608401526111598184018b8d611002565b9050828103608084015261116e81898b6110d4565b905060ff871660a084015282810360c084015261118b8187610a9e565b905067ffffffffffffffff851660e08401528281036101008401526111b08185610a9e565b9e9d5050505050505050505050505050565b60006101208d835273ffffffffffffffffffffffffffffffffffffffff8d16602084015267ffffffffffffffff808d16604085015281606085015261120a8285018c8e611002565b9150838203608085015261121f828a8c6110d4565b915060ff881660a085015283820360c085015261123c8288610a9e565b90861660e085015283810361010085015290506111b08185610a9e56fea164736f6c6343000818000a", + ABI: "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReportingUnsupported\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"previousConfigBlockNumber\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"configCount\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes[]\",\"name\":\"signers\",\"type\":\"bytes[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"onchainConfig\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"name\":\"Transmitted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDetails\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDigestAndEpoch\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"scanLogs\",\"type\":\"bool\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"_signers\",\"type\":\"bytes[]\"},{\"internalType\":\"address[]\",\"name\":\"_transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"_f\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"_onchainConfig\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"_offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"_offchainConfig\",\"type\":\"bytes\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"transmit\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b5033806000816100675760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615610097576100978161009f565b505050610148565b336001600160a01b038216036100f75760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161005e565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6112bc806101576000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c80638da5cb5b1161005b5780638da5cb5b14610169578063afcb95d714610191578063b1dc65a4146101b1578063f2fde38b146101c457600080fd5b8063181f5a771461008d57806379ba5097146100df5780637f3c87d3146100e957806381ff7048146100fc575b600080fd5b6100c96040518060400160405280600e81526020017f4b657973746f6e6520312e302e3000000000000000000000000000000000000081525081565b6040516100d69190610b8a565b60405180910390f35b6100e76101d7565b005b6100e76100f7366004610cf8565b6102d9565b61014660015460025463ffffffff74010000000000000000000000000000000000000000830481169378010000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff9485168152939092166020840152908201526060016100d6565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100d6565b6040805160018152600060208201819052918101919091526060016100d6565b6100e76101bf366004610dd4565b6108b7565b6100e76101d2366004610edd565b6108e9565b60015473ffffffffffffffffffffffffffffffffffffffff16331461025d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b868560ff8616601f83111561034a576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f746f6f206d616e79207369676e657273000000000000000000000000000000006044820152606401610254565b806000036103b4576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f66206d75737420626520706f73697469766500000000000000000000000000006044820152606401610254565b818314610442576040517f89a61989000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f6f7261636c6520616464726573736573206f7574206f6620726567697374726160448201527f74696f6e000000000000000000000000000000000000000000000000000000006064820152608401610254565b61044d816003610f27565b83116104b5576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f6661756c74792d6f7261636c65206620746f6f206869676800000000000000006044820152606401610254565b6104bd6108fd565b60005b8a8110156107255760008a8a838181106104dc576104dc610f44565b90506020020160208101906104f19190610edd565b73ffffffffffffffffffffffffffffffffffffffff160361056e576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f7472616e736d6974746572206d757374206e6f7420626520656d7074790000006044820152606401610254565b3660008d8d8481811061058357610583610f44565b90506020028101906105959190610f73565b9092509050600061ffff82165b8082101561071657806105b6836003610fd8565b111561061e576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f696e76616c6964207369676e6572207075624b657920656e636f64696e6700006044820152606401610254565b60006008858561062f866002610fd8565b81811061063e5761063e610f44565b919091013560f81c90911b90508585610658866001610fd8565b81811061066757610667610f44565b6106789392013560f81c9050610fd8565b90508181610687856003610fd8565b6106919190610fd8565b11156106f9576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f696e76616c6964207369676e6572207075624b657920656e636f64696e6700006044820152606401610254565b610704816003610fd8565b61070e9084610fd8565b9250506105a2565b505050508060010190506104c0565b50600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff8916179055600180547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff8116780100000000000000000000000000000000000000000000000063ffffffff43811682029290921780855592048116929182916014916107d491849174010000000000000000000000000000000000000000900416610feb565b92506101000a81548163ffffffff021916908363ffffffff16021790555061081d4630600160149054906101000a900463ffffffff1663ffffffff168f8f8f8f8f8f8f8f610980565b6002600001819055508b8b9050600260010160016101000a81548160ff021916908360ff1602179055507f36257c6e8d535293ad661e377c0baac536289be6707b8a488ac175ddaa4055c881600260000154600160149054906101000a900463ffffffff168f8f8f8f8f8f8f8f6040516108a19b9a9998979695949392919061117e565b60405180910390a1505050505050505050505050565b6040517f0750181900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6108f16108fd565b6108fa81610a31565b50565b60005473ffffffffffffffffffffffffffffffffffffffff16331461097e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610254565b565b6000808c8c8c8c8c8c8c8c8c8c8c6040516020016109a89b9a99989796959493929190611218565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e0e000000000000000000000000000000000000000000000000000000000000179150509b9a5050505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603610ab0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610254565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000815180845260005b81811015610b4c57602081850181015186830182015201610b30565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000610b9d6020830184610b26565b9392505050565b60008083601f840112610bb657600080fd5b50813567ffffffffffffffff811115610bce57600080fd5b6020830191508360208260051b8501011115610be957600080fd5b9250929050565b803560ff81168114610c0157600080fd5b919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f830112610c4657600080fd5b813567ffffffffffffffff80821115610c6157610c61610c06565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715610ca757610ca7610c06565b81604052838152866020858801011115610cc057600080fd5b836020870160208301376000602085830101528094505050505092915050565b803567ffffffffffffffff81168114610c0157600080fd5b60008060008060008060008060c0898b031215610d1457600080fd5b883567ffffffffffffffff80821115610d2c57600080fd5b610d388c838d01610ba4565b909a50985060208b0135915080821115610d5157600080fd5b610d5d8c838d01610ba4565b9098509650869150610d7160408c01610bf0565b955060608b0135915080821115610d8757600080fd5b610d938c838d01610c35565b9450610da160808c01610ce0565b935060a08b0135915080821115610db757600080fd5b50610dc48b828c01610c35565b9150509295985092959890939650565b60008060008060008060008060e0898b031215610df057600080fd5b606089018a811115610e0157600080fd5b8998503567ffffffffffffffff80821115610e1b57600080fd5b818b0191508b601f830112610e2f57600080fd5b813581811115610e3e57600080fd5b8c6020828501011115610e5057600080fd5b6020830199508098505060808b0135915080821115610e6e57600080fd5b610e7a8c838d01610ba4565b909750955060a08b0135915080821115610e9357600080fd5b50610ea08b828c01610ba4565b999c989b50969995989497949560c00135949350505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610c0157600080fd5b600060208284031215610eef57600080fd5b610b9d82610eb9565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082028115828204841417610f3e57610f3e610ef8565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610fa857600080fd5b83018035915067ffffffffffffffff821115610fc357600080fd5b602001915036819003821315610be957600080fd5b80820180821115610f3e57610f3e610ef8565b63ffffffff81811683821601908082111561100857611008610ef8565b5092915050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b6000838385526020808601955060208560051b8301018460005b8781101561111d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe085840301895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18836030181126110d357600080fd5b8701848101903567ffffffffffffffff8111156110ef57600080fd5b8036038213156110fe57600080fd5b61110985828461100f565b9a86019a9450505090830190600101611072565b5090979650505050505050565b8183526000602080850194508260005b858110156111735773ffffffffffffffffffffffffffffffffffffffff61116083610eb9565b168752958201959082019060010161113a565b509495945050505050565b600061012063ffffffff808f1684528d6020850152808d166040850152508060608401526111af8184018b8d611058565b905082810360808401526111c481898b61112a565b905060ff871660a084015282810360c08401526111e18187610b26565b905067ffffffffffffffff851660e08401528281036101008401526112068185610b26565b9e9d5050505050505050505050505050565b60006101208d835273ffffffffffffffffffffffffffffffffffffffff8d16602084015267ffffffffffffffff808d1660408501528160608501526112608285018c8e611058565b91508382036080850152611275828a8c61112a565b915060ff881660a085015283820360c08501526112928288610b26565b90861660e085015283810361010085015290506112068185610b2656fea164736f6c6343000818000a", } var OCR3CapabilityABI = OCR3CapabilityMetaData.ABI diff --git a/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 24d463c9b04..2ef8c73a6ce 100644 --- a/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,5 +1,5 @@ GETH_VERSION: 1.13.8 -capabilities_registry: ../../../contracts/solc/v0.8.24/CapabilitiesRegistry/CapabilitiesRegistry.abi ../../../contracts/solc/v0.8.24/CapabilitiesRegistry/CapabilitiesRegistry.bin 5c97ce78e6b13502b654c4ba0ff0ad6348a40435f709b6d157e6709d43360bbe +capabilities_registry: ../../../contracts/solc/v0.8.24/CapabilitiesRegistry/CapabilitiesRegistry.abi ../../../contracts/solc/v0.8.24/CapabilitiesRegistry/CapabilitiesRegistry.bin 2cd39422dd88c4b9b05f6b297739e587fcfed3b06b8929b7a489ac8f36d4fb6c feeds_consumer: ../../../contracts/solc/v0.8.24/KeystoneFeedsConsumer/KeystoneFeedsConsumer.abi ../../../contracts/solc/v0.8.24/KeystoneFeedsConsumer/KeystoneFeedsConsumer.bin 6ac5b12eff3b022a35c3c40d5ed0285bf9bfec0e3669a4b12307332a216048ca forwarder: ../../../contracts/solc/v0.8.24/KeystoneForwarder/KeystoneForwarder.abi ../../../contracts/solc/v0.8.24/KeystoneForwarder/KeystoneForwarder.bin 03911334d0c88f8ee8ee2d9832fd312bc8a48c824fcda5c807585af2d0e6a148 -ocr3_capability: ../../../contracts/solc/v0.8.24/OCR3Capability/OCR3Capability.abi ../../../contracts/solc/v0.8.24/OCR3Capability/OCR3Capability.bin 2a6bfae30ccf38327fc7e78605a226839dfa0ce5a1a22e0414b91d24c35b3a53 +ocr3_capability: ../../../contracts/solc/v0.8.24/OCR3Capability/OCR3Capability.abi ../../../contracts/solc/v0.8.24/OCR3Capability/OCR3Capability.bin 509af20993cc8d7f4e84d55a3f1316d4bb9ab706f9a50a91ac72de96dbc1244e diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index b60dd8d73ce..d3af33d85bf 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -514,8 +514,9 @@ func NewEthMocks(t testing.TB) *evmclimocks.Client { func NewEthMocksWithStartupAssertions(t testing.TB) *evmclimocks.Client { testutils.SkipShort(t, "long test") c := NewEthMocks(t) + chHead := make(<-chan *evmtypes.Head) c.On("Dial", mock.Anything).Maybe().Return(nil) - c.On("SubscribeNewHead", mock.Anything, mock.Anything).Maybe().Return(EmptyMockSubscription(t), nil) + c.On("SubscribeToHeads", mock.Anything).Maybe().Return(chHead, EmptyMockSubscription(t), nil) c.On("SendTransaction", mock.Anything, mock.Anything).Maybe().Return(nil) c.On("HeadByNumber", mock.Anything, mock.Anything).Maybe().Return(Head(0), nil) c.On("ConfiguredChainID").Maybe().Return(&FixtureChainID) @@ -536,8 +537,9 @@ func NewEthMocksWithStartupAssertions(t testing.TB) *evmclimocks.Client { func NewEthMocksWithTransactionsOnBlocksAssertions(t testing.TB) *evmclimocks.Client { testutils.SkipShort(t, "long test") c := NewEthMocks(t) + chHead := make(<-chan *evmtypes.Head) c.On("Dial", mock.Anything).Maybe().Return(nil) - c.On("SubscribeNewHead", mock.Anything, mock.Anything).Maybe().Return(EmptyMockSubscription(t), nil) + c.On("SubscribeToHeads", mock.Anything).Maybe().Return(chHead, EmptyMockSubscription(t), nil) c.On("SendTransaction", mock.Anything, mock.Anything).Maybe().Return(nil) c.On("SendTransactionReturnCode", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(client.Successful, nil) // Construct chain @@ -1297,7 +1299,7 @@ func MockApplicationEthCalls(t *testing.T, app *TestApplication, ethClient *evmc // Start ethClient.On("Dial", mock.Anything).Return(nil) - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything).Return(sub, nil).Maybe() + ethClient.On("SubscribeToHeads", mock.Anything).Return(make(<-chan *evmtypes.Head), sub, nil).Maybe() ethClient.On("ConfiguredChainID", mock.Anything).Return(evmtest.MustGetDefaultChainID(t, app.GetConfig().EVMConfigs()), nil) ethClient.On("PendingNonceAt", mock.Anything, mock.Anything).Return(uint64(0), nil).Maybe() ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(nil, nil).Maybe() diff --git a/core/internal/features/features_test.go b/core/internal/features/features_test.go index edd5db410ff..79af91796dd 100644 --- a/core/internal/features/features_test.go +++ b/core/internal/features/features_test.go @@ -1294,14 +1294,14 @@ func TestIntegration_BlockHistoryEstimator(t *testing.T) { h42 := evmtypes.Head{Hash: b42.Hash, ParentHash: h41.Hash, Number: 42, EVMChainID: evmChainID} mockEth := &evmtestutils.MockEth{EthClient: ethClient} - ethClient.On("SubscribeNewHead", mock.Anything, mock.Anything). + ethClient.On("SubscribeToHeads", mock.Anything). Return( - func(ctx context.Context, ch chan<- *evmtypes.Head) ethereum.Subscription { + func(ctx context.Context) (<-chan *evmtypes.Head, ethereum.Subscription, error) { + ch := make(chan *evmtypes.Head) sub := mockEth.NewSub(t) chchNewHeads <- evmtestutils.NewRawSub(ch, sub.Err()) - return sub + return ch, sub, nil }, - func(ctx context.Context, ch chan<- *evmtypes.Head) error { return nil }, ) // Nonce syncer ethClient.On("PendingNonceAt", mock.Anything, mock.Anything).Maybe().Return(uint64(0), nil) diff --git a/core/internal/features/ocr2/features_ocr2_plugin_test.go b/core/internal/features/ocr2/features_ocr2_plugin_test.go index 96a9f32e957..102f4188742 100644 --- a/core/internal/features/ocr2/features_ocr2_plugin_test.go +++ b/core/internal/features/ocr2/features_ocr2_plugin_test.go @@ -6,9 +6,11 @@ import ( "testing" "github.com/smartcontractkit/chainlink/v2/core/config/env" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" ) func TestIntegration_OCR2_plugins(t *testing.T) { t.Setenv(string(env.MedianPlugin.Cmd), "chainlink-feeds") + testutils.SkipFlakey(t, "https://smartcontract-it.atlassian.net/browse/BCF-3417") testIntegration_OCR2(t) } diff --git a/core/internal/features/ocr2/features_ocr2_test.go b/core/internal/features/ocr2/features_ocr2_test.go index 9160310261f..aad126b4078 100644 --- a/core/internal/features/ocr2/features_ocr2_test.go +++ b/core/internal/features/ocr2/features_ocr2_test.go @@ -190,6 +190,7 @@ func setupNodeOCR2( func TestIntegration_OCR2(t *testing.T) { t.Parallel() + testutils.SkipFlakey(t, "https://smartcontract-it.atlassian.net/browse/BCF-3417") testIntegration_OCR2(t) } diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 35db6742cb4..e2ed341146c 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -24,7 +24,7 @@ require ( github.com/prometheus/client_golang v1.20.0 github.com/shopspring/decimal v1.4.0 github.com/smartcontractkit/chainlink-automation v0.8.0 - github.com/smartcontractkit/chainlink-common v0.3.0 + github.com/smartcontractkit/chainlink-common v0.3.1-0.20241014122810-6c3cc4d0dc87 github.com/smartcontractkit/chainlink/integration-tests v0.0.0-00010101000000-000000000000 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20241007185508-adbe57025f12 @@ -34,7 +34,7 @@ require ( github.com/umbracle/ethgo v0.1.3 github.com/umbracle/fastrlp v0.0.0-20220527094140-59d5dd30e722 github.com/urfave/cli v1.22.14 - google.golang.org/protobuf v1.34.2 + google.golang.org/protobuf v1.35.1 k8s.io/api v0.31.0 k8s.io/apimachinery v0.31.0 k8s.io/client-go v0.31.0 @@ -286,11 +286,12 @@ require ( github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/shirou/gopsutil/v3 v3.24.3 // indirect github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86 // indirect - github.com/smartcontractkit/chain-selectors v1.0.23 // indirect - github.com/smartcontractkit/chainlink-ccip v0.0.0-20241010120731-ae3e8f4935a0 // indirect + github.com/smartcontractkit/chain-selectors v1.0.27 // indirect + github.com/smartcontractkit/chainlink-ccip v0.0.0-20241014104242-9227e5c976a7 // indirect github.com/smartcontractkit/chainlink-cosmos v0.5.1 // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.0 // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect + github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 // indirect github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241010140936-4e1d0ae8315a // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.1.0 // indirect github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect @@ -372,7 +373,7 @@ require ( google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect - google.golang.org/grpc v1.65.0 // indirect + google.golang.org/grpc v1.66.2 // indirect gopkg.in/guregu/null.v4 v4.0.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 9ef8d9255e6..a523c075b2e 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1068,20 +1068,22 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86 h1:qQH6fZZe31nBAG6INHph3z5ysDTPptyu0TR9uoJ1+ok= github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86/go.mod h1:WtWOoVQQEHxRHL2hNmuRrvDfYfQG/CioFNoa9Rr2mBE= -github.com/smartcontractkit/chain-selectors v1.0.23 h1:D2Eaex4Cw/O7Lg3tX6WklOqnjjIQAEBnutCtksPzVDY= -github.com/smartcontractkit/chain-selectors v1.0.23/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= +github.com/smartcontractkit/chain-selectors v1.0.27 h1:VE/ftX9Aae4gnw67yR1raKi+30iWKL/sWq8uyiLHM8k= +github.com/smartcontractkit/chain-selectors v1.0.27/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v0.8.0 h1:hFz2EHU06bkEfhcqhK8JdjKTWpDOr0XJ6xL9oELDoUg= github.com/smartcontractkit/chainlink-automation v0.8.0/go.mod h1:ObdjDfgGIaiE48Bb3yYcx1CeGBm392WlEw92U83LlUA= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241010120731-ae3e8f4935a0 h1:nKfjG9fufMQspKO0hFGK2B9ICL+tgajwP+nAXYP0LPc= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241010120731-ae3e8f4935a0/go.mod h1:DjyfH0AGcqctKB6DaRUpRL6GJ2SbrpiaBkA4h24/oMY= -github.com/smartcontractkit/chainlink-common v0.3.0 h1:mUXHBzzw2qPKyw6gPAC8JhO+ryT8maY+rBi9NFtqEy0= -github.com/smartcontractkit/chainlink-common v0.3.0/go.mod h1:tsGgeEJc5SUSlfVGSX0wR0EkRU3pM58D6SKF97V68ko= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241014104242-9227e5c976a7 h1:aMG3BllvgeL/vsqkebuAhWoIWOnitKnN1VxibdzGnYo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241014104242-9227e5c976a7/go.mod h1:H4BTXnZBhwRdsAFjqWZpB1/f3IZnuB/Ql7pXPmokzXg= +github.com/smartcontractkit/chainlink-common v0.3.1-0.20241014122810-6c3cc4d0dc87 h1:48qauRZcdxAOrgeko4RTm9ti4GGbSfzkcI4Dr/1FmjU= +github.com/smartcontractkit/chainlink-common v0.3.1-0.20241014122810-6c3cc4d0dc87/go.mod h1:tsGgeEJc5SUSlfVGSX0wR0EkRU3pM58D6SKF97V68ko= github.com/smartcontractkit/chainlink-cosmos v0.5.1 h1:2xeZWh+4/w7xalTdAu8jqgFuxZ291aYTEwZhlQEv/BY= github.com/smartcontractkit/chainlink-cosmos v0.5.1/go.mod h1:c1wUtVxXUqW4PzuCQhuHaBDZFv9XAQjhKTqam7GLGIU= github.com/smartcontractkit/chainlink-data-streams v0.1.0 h1:wcRJRm7eqfbgN+Na+GjAe0/IUn6XwmSagFHqIWHHBGk= github.com/smartcontractkit/chainlink-data-streams v0.1.0/go.mod h1:lmdRVjg49Do+5tkk9V5iAhi+Jm2kXhjZXWAbzh7xg7o= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4= +github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 h1:PBUaFfPLm+Efq7H9kdfGBivH+QhJ6vB5EZTR/sCZsxI= +github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0/go.mod h1:m/A3lqD7ms/RsQ9BT5P2uceYY0QX5mIt4KQxT2G6qEo= github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241010140936-4e1d0ae8315a h1:WdteRQ8p+4m9VPA5ibwheQBeBd1ndy1YlE6y0K/qeVE= github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241010140936-4e1d0ae8315a/go.mod h1:XDrfLscHNHXIrB8MJVEYRcCVxxxO4BflcS+S6rlcgU4= github.com/smartcontractkit/chainlink-starknet/relayer v0.1.0 h1:C00zDQ6AQdR9JFrHnOBEhC2TlYVzVSsC7k5AZ7hXwHI= @@ -1680,8 +1682,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo= +google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1694,8 +1696,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/core/services/chainlink/mocks/relayer_chain_interoperators.go b/core/services/chainlink/mocks/relayer_chain_interoperators.go index 5b0815b6569..13fef0e3180 100644 --- a/core/services/chainlink/mocks/relayer_chain_interoperators.go +++ b/core/services/chainlink/mocks/relayer_chain_interoperators.go @@ -17,7 +17,7 @@ import ( // FakeRelayerChainInteroperators is a fake chainlink.RelayerChainInteroperators. // This exists because mockery generation doesn't understand how to produce an alias instead of the underlying type (which is not exported in this case). type FakeRelayerChainInteroperators struct { - Relayers []loop.Relayer + Relayers map[types.RelayID]loop.Relayer EVMChains legacyevm.LegacyChainContainer Nodes []types.NodeStatus NodesErr error @@ -44,11 +44,17 @@ func (f *FakeRelayerChainInteroperators) Get(id types.RelayID) (loop.Relayer, er } func (f *FakeRelayerChainInteroperators) GetIDToRelayerMap() (map[types.RelayID]loop.Relayer, error) { - panic("unimplemented") + return f.Relayers, nil } func (f *FakeRelayerChainInteroperators) Slice() []loop.Relayer { - return f.Relayers + var relayers []loop.Relayer + + for _, value := range f.Relayers { + relayers = append(relayers, value) + } + + return relayers } func (f *FakeRelayerChainInteroperators) LegacyCosmosChains() chainlink.LegacyCosmosContainer { diff --git a/core/services/feeds/connection_manager.go b/core/services/feeds/connection_manager.go index aadfc41bd5e..df580c51c59 100644 --- a/core/services/feeds/connection_manager.go +++ b/core/services/feeds/connection_manager.go @@ -10,9 +10,9 @@ import ( "github.com/smartcontractkit/wsrpc" "github.com/smartcontractkit/chainlink-common/pkg/services" + pb "github.com/smartcontractkit/chainlink-protos/orchestrator/feedsmanager" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/recovery" - pb "github.com/smartcontractkit/chainlink/v2/core/services/feeds/proto" ) type ConnectionsManager interface { diff --git a/core/services/feeds/mocks/connections_manager.go b/core/services/feeds/mocks/connections_manager.go index 38bc47734bc..2e47bb19d62 100644 --- a/core/services/feeds/mocks/connections_manager.go +++ b/core/services/feeds/mocks/connections_manager.go @@ -3,8 +3,8 @@ package mocks import ( + feedsmanager "github.com/smartcontractkit/chainlink-protos/orchestrator/feedsmanager" feeds "github.com/smartcontractkit/chainlink/v2/core/services/feeds" - feedsmanager "github.com/smartcontractkit/chainlink/v2/core/services/feeds/proto" mock "github.com/stretchr/testify/mock" ) diff --git a/core/services/feeds/mocks/feeds_manager_client.go b/core/services/feeds/mocks/feeds_manager_client.go index f8d3da9b05c..3a90baafc92 100644 --- a/core/services/feeds/mocks/feeds_manager_client.go +++ b/core/services/feeds/mocks/feeds_manager_client.go @@ -5,7 +5,7 @@ package mocks import ( context "context" - feedsmanager "github.com/smartcontractkit/chainlink/v2/core/services/feeds/proto" + feedsmanager "github.com/smartcontractkit/chainlink-protos/orchestrator/feedsmanager" mock "github.com/stretchr/testify/mock" ) diff --git a/core/services/feeds/models.go b/core/services/feeds/models.go index 3fedc98d0f1..a4fbe8745f0 100644 --- a/core/services/feeds/models.go +++ b/core/services/feeds/models.go @@ -11,6 +11,7 @@ import ( "github.com/pkg/errors" "gopkg.in/guregu/null.v4" + proto "github.com/smartcontractkit/chainlink-protos/orchestrator/feedsmanager" "github.com/smartcontractkit/chainlink/v2/core/utils/crypto" ) @@ -77,7 +78,9 @@ type ChainType string const ( ChainTypeUnknown ChainType = "UNKNOWN" + ChainTypeAptos ChainType = "APTOS" ChainTypeEVM ChainType = "EVM" + ChainTypeSolana ChainType = "SOLANA" ChainTypeStarknet ChainType = "STARKNET" ) @@ -87,11 +90,24 @@ func NewChainType(s string) (ChainType, error) { return ChainTypeEVM, nil case "STARKNET": return ChainTypeStarknet, nil + case "SOLANA": + return ChainTypeSolana, nil + case "APTOS": + return ChainTypeAptos, nil default: return ChainTypeUnknown, errors.New("invalid chain type") } } +// ChainTypeToProtoChainType converts a ChainType to a proto.ChainType. +func ChainTypeToProtoChainType(chainType ChainType) proto.ChainType { + prefixed := "CHAIN_TYPE_" + string(chainType) + if chainType, exists := proto.ChainType_value[prefixed]; exists { + return proto.ChainType(chainType) + } + return proto.ChainType_CHAIN_TYPE_UNSPECIFIED +} + // FeedsManager defines a registered Feeds Manager Service and the connection // information. type FeedsManager struct { diff --git a/core/services/feeds/proto/feedsmanager.pb.go b/core/services/feeds/proto/feedsmanager.pb.go deleted file mode 100644 index c92de9b7faa..00000000000 --- a/core/services/feeds/proto/feedsmanager.pb.go +++ /dev/null @@ -1,2405 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.34.2 -// protoc v5.28.1 -// source: orchestrator/feedsmanager/feedsmanager.proto - -package feedsmanager - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// Defines the allowed job types -type JobType int32 - -const ( - JobType_JOB_TYPE_UNSPECIFIED JobType = 0 - JobType_JOB_TYPE_FLUX_MONITOR JobType = 1 - JobType_JOB_TYPE_OCR JobType = 2 - JobType_JOB_TYPE_OCR2 JobType = 3 -) - -// Enum value maps for JobType. -var ( - JobType_name = map[int32]string{ - 0: "JOB_TYPE_UNSPECIFIED", - 1: "JOB_TYPE_FLUX_MONITOR", - 2: "JOB_TYPE_OCR", - 3: "JOB_TYPE_OCR2", - } - JobType_value = map[string]int32{ - "JOB_TYPE_UNSPECIFIED": 0, - "JOB_TYPE_FLUX_MONITOR": 1, - "JOB_TYPE_OCR": 2, - "JOB_TYPE_OCR2": 3, - } -) - -func (x JobType) Enum() *JobType { - p := new(JobType) - *p = x - return p -} - -func (x JobType) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (JobType) Descriptor() protoreflect.EnumDescriptor { - return file_orchestrator_feedsmanager_feedsmanager_proto_enumTypes[0].Descriptor() -} - -func (JobType) Type() protoreflect.EnumType { - return &file_orchestrator_feedsmanager_feedsmanager_proto_enumTypes[0] -} - -func (x JobType) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use JobType.Descriptor instead. -func (JobType) EnumDescriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{0} -} - -type ChainType int32 - -const ( - ChainType_CHAIN_TYPE_UNSPECIFIED ChainType = 0 - ChainType_CHAIN_TYPE_EVM ChainType = 1 - ChainType_CHAIN_TYPE_SOLANA ChainType = 2 - ChainType_CHAIN_TYPE_STARKNET ChainType = 3 - ChainType_CHAIN_TYPE_APTOS ChainType = 4 -) - -// Enum value maps for ChainType. -var ( - ChainType_name = map[int32]string{ - 0: "CHAIN_TYPE_UNSPECIFIED", - 1: "CHAIN_TYPE_EVM", - 2: "CHAIN_TYPE_SOLANA", - 3: "CHAIN_TYPE_STARKNET", - 4: "CHAIN_TYPE_APTOS", - } - ChainType_value = map[string]int32{ - "CHAIN_TYPE_UNSPECIFIED": 0, - "CHAIN_TYPE_EVM": 1, - "CHAIN_TYPE_SOLANA": 2, - "CHAIN_TYPE_STARKNET": 3, - "CHAIN_TYPE_APTOS": 4, - } -) - -func (x ChainType) Enum() *ChainType { - p := new(ChainType) - *p = x - return p -} - -func (x ChainType) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ChainType) Descriptor() protoreflect.EnumDescriptor { - return file_orchestrator_feedsmanager_feedsmanager_proto_enumTypes[1].Descriptor() -} - -func (ChainType) Type() protoreflect.EnumType { - return &file_orchestrator_feedsmanager_feedsmanager_proto_enumTypes[1] -} - -func (x ChainType) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ChainType.Descriptor instead. -func (ChainType) EnumDescriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{1} -} - -type Chain struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Type ChainType `protobuf:"varint,2,opt,name=type,proto3,enum=cfm.ChainType" json:"type,omitempty"` -} - -func (x *Chain) Reset() { - *x = Chain{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Chain) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Chain) ProtoMessage() {} - -func (x *Chain) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Chain.ProtoReflect.Descriptor instead. -func (*Chain) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{0} -} - -func (x *Chain) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *Chain) GetType() ChainType { - if x != nil { - return x.Type - } - return ChainType_CHAIN_TYPE_UNSPECIFIED -} - -// An account on a specific blockchain -type Account struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ChainType ChainType `protobuf:"varint,1,opt,name=chain_type,json=chainType,proto3,enum=cfm.ChainType" json:"chain_type,omitempty"` - ChainId string `protobuf:"bytes,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` - Address string `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"` -} - -func (x *Account) Reset() { - *x = Account{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Account) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Account) ProtoMessage() {} - -func (x *Account) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Account.ProtoReflect.Descriptor instead. -func (*Account) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{1} -} - -func (x *Account) GetChainType() ChainType { - if x != nil { - return x.ChainType - } - return ChainType_CHAIN_TYPE_UNSPECIFIED -} - -func (x *Account) GetChainId() string { - if x != nil { - return x.ChainId - } - return "" -} - -func (x *Account) GetAddress() string { - if x != nil { - return x.Address - } - return "" -} - -// The config for Flux Monitor on a specific chain -type FluxMonitorConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` -} - -func (x *FluxMonitorConfig) Reset() { - *x = FluxMonitorConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FluxMonitorConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FluxMonitorConfig) ProtoMessage() {} - -func (x *FluxMonitorConfig) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FluxMonitorConfig.ProtoReflect.Descriptor instead. -func (*FluxMonitorConfig) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{2} -} - -func (x *FluxMonitorConfig) GetEnabled() bool { - if x != nil { - return x.Enabled - } - return false -} - -// The config for OCR1 on a specific chain -type OCR1Config struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - IsBootstrap bool `protobuf:"varint,2,opt,name=is_bootstrap,json=isBootstrap,proto3" json:"is_bootstrap,omitempty"` - P2PKeyBundle *OCR1Config_P2PKeyBundle `protobuf:"bytes,3,opt,name=p2p_key_bundle,json=p2pKeyBundle,proto3" json:"p2p_key_bundle,omitempty"` - OcrKeyBundle *OCR1Config_OCRKeyBundle `protobuf:"bytes,4,opt,name=ocr_key_bundle,json=ocrKeyBundle,proto3" json:"ocr_key_bundle,omitempty"` - Multiaddr string `protobuf:"bytes,5,opt,name=multiaddr,proto3" json:"multiaddr,omitempty"` -} - -func (x *OCR1Config) Reset() { - *x = OCR1Config{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *OCR1Config) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OCR1Config) ProtoMessage() {} - -func (x *OCR1Config) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use OCR1Config.ProtoReflect.Descriptor instead. -func (*OCR1Config) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{3} -} - -func (x *OCR1Config) GetEnabled() bool { - if x != nil { - return x.Enabled - } - return false -} - -func (x *OCR1Config) GetIsBootstrap() bool { - if x != nil { - return x.IsBootstrap - } - return false -} - -func (x *OCR1Config) GetP2PKeyBundle() *OCR1Config_P2PKeyBundle { - if x != nil { - return x.P2PKeyBundle - } - return nil -} - -func (x *OCR1Config) GetOcrKeyBundle() *OCR1Config_OCRKeyBundle { - if x != nil { - return x.OcrKeyBundle - } - return nil -} - -func (x *OCR1Config) GetMultiaddr() string { - if x != nil { - return x.Multiaddr - } - return "" -} - -// The config for OCR2 on a specific chain -type OCR2Config struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - IsBootstrap bool `protobuf:"varint,2,opt,name=is_bootstrap,json=isBootstrap,proto3" json:"is_bootstrap,omitempty"` - P2PKeyBundle *OCR2Config_P2PKeyBundle `protobuf:"bytes,3,opt,name=p2p_key_bundle,json=p2pKeyBundle,proto3" json:"p2p_key_bundle,omitempty"` - OcrKeyBundle *OCR2Config_OCRKeyBundle `protobuf:"bytes,4,opt,name=ocr_key_bundle,json=ocrKeyBundle,proto3" json:"ocr_key_bundle,omitempty"` - Multiaddr string `protobuf:"bytes,5,opt,name=multiaddr,proto3" json:"multiaddr,omitempty"` - Plugins *OCR2Config_Plugins `protobuf:"bytes,6,opt,name=plugins,proto3" json:"plugins,omitempty"` - ForwarderAddress *string `protobuf:"bytes,7,opt,name=forwarder_address,json=forwarderAddress,proto3,oneof" json:"forwarder_address,omitempty"` -} - -func (x *OCR2Config) Reset() { - *x = OCR2Config{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *OCR2Config) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OCR2Config) ProtoMessage() {} - -func (x *OCR2Config) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use OCR2Config.ProtoReflect.Descriptor instead. -func (*OCR2Config) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{4} -} - -func (x *OCR2Config) GetEnabled() bool { - if x != nil { - return x.Enabled - } - return false -} - -func (x *OCR2Config) GetIsBootstrap() bool { - if x != nil { - return x.IsBootstrap - } - return false -} - -func (x *OCR2Config) GetP2PKeyBundle() *OCR2Config_P2PKeyBundle { - if x != nil { - return x.P2PKeyBundle - } - return nil -} - -func (x *OCR2Config) GetOcrKeyBundle() *OCR2Config_OCRKeyBundle { - if x != nil { - return x.OcrKeyBundle - } - return nil -} - -func (x *OCR2Config) GetMultiaddr() string { - if x != nil { - return x.Multiaddr - } - return "" -} - -func (x *OCR2Config) GetPlugins() *OCR2Config_Plugins { - if x != nil { - return x.Plugins - } - return nil -} - -func (x *OCR2Config) GetForwarderAddress() string { - if x != nil && x.ForwarderAddress != nil { - return *x.ForwarderAddress - } - return "" -} - -type ChainConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Chain *Chain `protobuf:"bytes,1,opt,name=chain,proto3" json:"chain,omitempty"` - AccountAddress string `protobuf:"bytes,2,opt,name=account_address,json=accountAddress,proto3" json:"account_address,omitempty"` - AdminAddress string `protobuf:"bytes,3,opt,name=admin_address,json=adminAddress,proto3" json:"admin_address,omitempty"` - FluxMonitorConfig *FluxMonitorConfig `protobuf:"bytes,4,opt,name=flux_monitor_config,json=fluxMonitorConfig,proto3" json:"flux_monitor_config,omitempty"` - Ocr1Config *OCR1Config `protobuf:"bytes,5,opt,name=ocr1_config,json=ocr1Config,proto3" json:"ocr1_config,omitempty"` - Ocr2Config *OCR2Config `protobuf:"bytes,6,opt,name=ocr2_config,json=ocr2Config,proto3" json:"ocr2_config,omitempty"` - // For EVM chains, we do not need this value and it is kept in the node's - // keystore. For starknet, because the wallet address needs to be deployed - // using this value and this pub key needs to be passed into the starknet - // relayer, we request the node to send this directly to CLO. - AccountAddressPublicKey *string `protobuf:"bytes,7,opt,name=account_address_public_key,json=accountAddressPublicKey,proto3,oneof" json:"account_address_public_key,omitempty"` -} - -func (x *ChainConfig) Reset() { - *x = ChainConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ChainConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ChainConfig) ProtoMessage() {} - -func (x *ChainConfig) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ChainConfig.ProtoReflect.Descriptor instead. -func (*ChainConfig) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{5} -} - -func (x *ChainConfig) GetChain() *Chain { - if x != nil { - return x.Chain - } - return nil -} - -func (x *ChainConfig) GetAccountAddress() string { - if x != nil { - return x.AccountAddress - } - return "" -} - -func (x *ChainConfig) GetAdminAddress() string { - if x != nil { - return x.AdminAddress - } - return "" -} - -func (x *ChainConfig) GetFluxMonitorConfig() *FluxMonitorConfig { - if x != nil { - return x.FluxMonitorConfig - } - return nil -} - -func (x *ChainConfig) GetOcr1Config() *OCR1Config { - if x != nil { - return x.Ocr1Config - } - return nil -} - -func (x *ChainConfig) GetOcr2Config() *OCR2Config { - if x != nil { - return x.Ocr2Config - } - return nil -} - -func (x *ChainConfig) GetAccountAddressPublicKey() string { - if x != nil && x.AccountAddressPublicKey != nil { - return *x.AccountAddressPublicKey - } - return "" -} - -type UpdateNodeRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - JobTypes []JobType `protobuf:"varint,1,rep,packed,name=job_types,json=jobTypes,proto3,enum=cfm.JobType" json:"job_types,omitempty"` - ChainId int64 `protobuf:"varint,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` // To be removed when all nodes are upgraded to 1.2 - AccountAddresses []string `protobuf:"bytes,3,rep,name=account_addresses,json=accountAddresses,proto3" json:"account_addresses,omitempty"` - IsBootstrapPeer bool `protobuf:"varint,4,opt,name=is_bootstrap_peer,json=isBootstrapPeer,proto3" json:"is_bootstrap_peer,omitempty"` - BootstrapMultiaddr string `protobuf:"bytes,5,opt,name=bootstrap_multiaddr,json=bootstrapMultiaddr,proto3" json:"bootstrap_multiaddr,omitempty"` - Version string `protobuf:"bytes,6,opt,name=version,proto3" json:"version,omitempty"` - ChainIds []int64 `protobuf:"varint,7,rep,packed,name=chain_ids,json=chainIds,proto3" json:"chain_ids,omitempty"` - Accounts []*Account `protobuf:"bytes,8,rep,name=accounts,proto3" json:"accounts,omitempty"` - Chains []*Chain `protobuf:"bytes,9,rep,name=chains,proto3" json:"chains,omitempty"` - ChainConfigs []*ChainConfig `protobuf:"bytes,10,rep,name=chain_configs,json=chainConfigs,proto3" json:"chain_configs,omitempty"` -} - -func (x *UpdateNodeRequest) Reset() { - *x = UpdateNodeRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *UpdateNodeRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UpdateNodeRequest) ProtoMessage() {} - -func (x *UpdateNodeRequest) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UpdateNodeRequest.ProtoReflect.Descriptor instead. -func (*UpdateNodeRequest) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{6} -} - -func (x *UpdateNodeRequest) GetJobTypes() []JobType { - if x != nil { - return x.JobTypes - } - return nil -} - -func (x *UpdateNodeRequest) GetChainId() int64 { - if x != nil { - return x.ChainId - } - return 0 -} - -func (x *UpdateNodeRequest) GetAccountAddresses() []string { - if x != nil { - return x.AccountAddresses - } - return nil -} - -func (x *UpdateNodeRequest) GetIsBootstrapPeer() bool { - if x != nil { - return x.IsBootstrapPeer - } - return false -} - -func (x *UpdateNodeRequest) GetBootstrapMultiaddr() string { - if x != nil { - return x.BootstrapMultiaddr - } - return "" -} - -func (x *UpdateNodeRequest) GetVersion() string { - if x != nil { - return x.Version - } - return "" -} - -func (x *UpdateNodeRequest) GetChainIds() []int64 { - if x != nil { - return x.ChainIds - } - return nil -} - -func (x *UpdateNodeRequest) GetAccounts() []*Account { - if x != nil { - return x.Accounts - } - return nil -} - -func (x *UpdateNodeRequest) GetChains() []*Chain { - if x != nil { - return x.Chains - } - return nil -} - -func (x *UpdateNodeRequest) GetChainConfigs() []*ChainConfig { - if x != nil { - return x.ChainConfigs - } - return nil -} - -type UpdateNodeResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *UpdateNodeResponse) Reset() { - *x = UpdateNodeResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *UpdateNodeResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UpdateNodeResponse) ProtoMessage() {} - -func (x *UpdateNodeResponse) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UpdateNodeResponse.ProtoReflect.Descriptor instead. -func (*UpdateNodeResponse) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{7} -} - -type ApprovedJobRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Uuid string `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"` - Version int64 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` -} - -func (x *ApprovedJobRequest) Reset() { - *x = ApprovedJobRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ApprovedJobRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ApprovedJobRequest) ProtoMessage() {} - -func (x *ApprovedJobRequest) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ApprovedJobRequest.ProtoReflect.Descriptor instead. -func (*ApprovedJobRequest) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{8} -} - -func (x *ApprovedJobRequest) GetUuid() string { - if x != nil { - return x.Uuid - } - return "" -} - -func (x *ApprovedJobRequest) GetVersion() int64 { - if x != nil { - return x.Version - } - return 0 -} - -type ApprovedJobResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *ApprovedJobResponse) Reset() { - *x = ApprovedJobResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ApprovedJobResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ApprovedJobResponse) ProtoMessage() {} - -func (x *ApprovedJobResponse) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ApprovedJobResponse.ProtoReflect.Descriptor instead. -func (*ApprovedJobResponse) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{9} -} - -type HealthcheckRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *HealthcheckRequest) Reset() { - *x = HealthcheckRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *HealthcheckRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HealthcheckRequest) ProtoMessage() {} - -func (x *HealthcheckRequest) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use HealthcheckRequest.ProtoReflect.Descriptor instead. -func (*HealthcheckRequest) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{10} -} - -type HealthcheckResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *HealthcheckResponse) Reset() { - *x = HealthcheckResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *HealthcheckResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HealthcheckResponse) ProtoMessage() {} - -func (x *HealthcheckResponse) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use HealthcheckResponse.ProtoReflect.Descriptor instead. -func (*HealthcheckResponse) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{11} -} - -type RejectedJobRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Uuid string `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"` - Version int64 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` -} - -func (x *RejectedJobRequest) Reset() { - *x = RejectedJobRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RejectedJobRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RejectedJobRequest) ProtoMessage() {} - -func (x *RejectedJobRequest) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[12] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RejectedJobRequest.ProtoReflect.Descriptor instead. -func (*RejectedJobRequest) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{12} -} - -func (x *RejectedJobRequest) GetUuid() string { - if x != nil { - return x.Uuid - } - return "" -} - -func (x *RejectedJobRequest) GetVersion() int64 { - if x != nil { - return x.Version - } - return 0 -} - -type RejectedJobResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *RejectedJobResponse) Reset() { - *x = RejectedJobResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RejectedJobResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RejectedJobResponse) ProtoMessage() {} - -func (x *RejectedJobResponse) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[13] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RejectedJobResponse.ProtoReflect.Descriptor instead. -func (*RejectedJobResponse) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{13} -} - -type CancelledJobRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Uuid string `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"` - Version int64 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` -} - -func (x *CancelledJobRequest) Reset() { - *x = CancelledJobRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CancelledJobRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CancelledJobRequest) ProtoMessage() {} - -func (x *CancelledJobRequest) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[14] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CancelledJobRequest.ProtoReflect.Descriptor instead. -func (*CancelledJobRequest) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{14} -} - -func (x *CancelledJobRequest) GetUuid() string { - if x != nil { - return x.Uuid - } - return "" -} - -func (x *CancelledJobRequest) GetVersion() int64 { - if x != nil { - return x.Version - } - return 0 -} - -type CancelledJobResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *CancelledJobResponse) Reset() { - *x = CancelledJobResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CancelledJobResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CancelledJobResponse) ProtoMessage() {} - -func (x *CancelledJobResponse) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[15] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CancelledJobResponse.ProtoReflect.Descriptor instead. -func (*CancelledJobResponse) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{15} -} - -type ProposeJobRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Spec string `protobuf:"bytes,2,opt,name=spec,proto3" json:"spec,omitempty"` - Multiaddrs []string `protobuf:"bytes,3,rep,name=multiaddrs,proto3" json:"multiaddrs,omitempty"` - Version int64 `protobuf:"varint,4,opt,name=version,proto3" json:"version,omitempty"` -} - -func (x *ProposeJobRequest) Reset() { - *x = ProposeJobRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[16] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ProposeJobRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProposeJobRequest) ProtoMessage() {} - -func (x *ProposeJobRequest) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[16] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProposeJobRequest.ProtoReflect.Descriptor instead. -func (*ProposeJobRequest) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{16} -} - -func (x *ProposeJobRequest) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *ProposeJobRequest) GetSpec() string { - if x != nil { - return x.Spec - } - return "" -} - -func (x *ProposeJobRequest) GetMultiaddrs() []string { - if x != nil { - return x.Multiaddrs - } - return nil -} - -func (x *ProposeJobRequest) GetVersion() int64 { - if x != nil { - return x.Version - } - return 0 -} - -type ProposeJobResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` -} - -func (x *ProposeJobResponse) Reset() { - *x = ProposeJobResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[17] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ProposeJobResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProposeJobResponse) ProtoMessage() {} - -func (x *ProposeJobResponse) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[17] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProposeJobResponse.ProtoReflect.Descriptor instead. -func (*ProposeJobResponse) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{17} -} - -func (x *ProposeJobResponse) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -type DeleteJobRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` -} - -func (x *DeleteJobRequest) Reset() { - *x = DeleteJobRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[18] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DeleteJobRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteJobRequest) ProtoMessage() {} - -func (x *DeleteJobRequest) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[18] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteJobRequest.ProtoReflect.Descriptor instead. -func (*DeleteJobRequest) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{18} -} - -func (x *DeleteJobRequest) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -type DeleteJobResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` -} - -func (x *DeleteJobResponse) Reset() { - *x = DeleteJobResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[19] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DeleteJobResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteJobResponse) ProtoMessage() {} - -func (x *DeleteJobResponse) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[19] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteJobResponse.ProtoReflect.Descriptor instead. -func (*DeleteJobResponse) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{19} -} - -func (x *DeleteJobResponse) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -type RevokeJobRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` -} - -func (x *RevokeJobRequest) Reset() { - *x = RevokeJobRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[20] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RevokeJobRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RevokeJobRequest) ProtoMessage() {} - -func (x *RevokeJobRequest) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[20] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RevokeJobRequest.ProtoReflect.Descriptor instead. -func (*RevokeJobRequest) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{20} -} - -func (x *RevokeJobRequest) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -type RevokeJobResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` -} - -func (x *RevokeJobResponse) Reset() { - *x = RevokeJobResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[21] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RevokeJobResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RevokeJobResponse) ProtoMessage() {} - -func (x *RevokeJobResponse) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[21] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RevokeJobResponse.ProtoReflect.Descriptor instead. -func (*RevokeJobResponse) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{21} -} - -func (x *RevokeJobResponse) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -type OCR1Config_P2PKeyBundle struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - PeerId string `protobuf:"bytes,1,opt,name=peer_id,json=peerId,proto3" json:"peer_id,omitempty"` - PublicKey string `protobuf:"bytes,2,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` -} - -func (x *OCR1Config_P2PKeyBundle) Reset() { - *x = OCR1Config_P2PKeyBundle{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[22] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *OCR1Config_P2PKeyBundle) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OCR1Config_P2PKeyBundle) ProtoMessage() {} - -func (x *OCR1Config_P2PKeyBundle) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[22] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use OCR1Config_P2PKeyBundle.ProtoReflect.Descriptor instead. -func (*OCR1Config_P2PKeyBundle) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{3, 0} -} - -func (x *OCR1Config_P2PKeyBundle) GetPeerId() string { - if x != nil { - return x.PeerId - } - return "" -} - -func (x *OCR1Config_P2PKeyBundle) GetPublicKey() string { - if x != nil { - return x.PublicKey - } - return "" -} - -type OCR1Config_OCRKeyBundle struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - BundleId string `protobuf:"bytes,1,opt,name=bundle_id,json=bundleId,proto3" json:"bundle_id,omitempty"` - ConfigPublicKey string `protobuf:"bytes,2,opt,name=config_public_key,json=configPublicKey,proto3" json:"config_public_key,omitempty"` - OffchainPublicKey string `protobuf:"bytes,3,opt,name=offchain_public_key,json=offchainPublicKey,proto3" json:"offchain_public_key,omitempty"` - OnchainSigningAddress string `protobuf:"bytes,4,opt,name=onchain_signing_address,json=onchainSigningAddress,proto3" json:"onchain_signing_address,omitempty"` -} - -func (x *OCR1Config_OCRKeyBundle) Reset() { - *x = OCR1Config_OCRKeyBundle{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[23] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *OCR1Config_OCRKeyBundle) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OCR1Config_OCRKeyBundle) ProtoMessage() {} - -func (x *OCR1Config_OCRKeyBundle) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[23] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use OCR1Config_OCRKeyBundle.ProtoReflect.Descriptor instead. -func (*OCR1Config_OCRKeyBundle) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{3, 1} -} - -func (x *OCR1Config_OCRKeyBundle) GetBundleId() string { - if x != nil { - return x.BundleId - } - return "" -} - -func (x *OCR1Config_OCRKeyBundle) GetConfigPublicKey() string { - if x != nil { - return x.ConfigPublicKey - } - return "" -} - -func (x *OCR1Config_OCRKeyBundle) GetOffchainPublicKey() string { - if x != nil { - return x.OffchainPublicKey - } - return "" -} - -func (x *OCR1Config_OCRKeyBundle) GetOnchainSigningAddress() string { - if x != nil { - return x.OnchainSigningAddress - } - return "" -} - -type OCR2Config_P2PKeyBundle struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - PeerId string `protobuf:"bytes,1,opt,name=peer_id,json=peerId,proto3" json:"peer_id,omitempty"` - PublicKey string `protobuf:"bytes,2,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` -} - -func (x *OCR2Config_P2PKeyBundle) Reset() { - *x = OCR2Config_P2PKeyBundle{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[24] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *OCR2Config_P2PKeyBundle) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OCR2Config_P2PKeyBundle) ProtoMessage() {} - -func (x *OCR2Config_P2PKeyBundle) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[24] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use OCR2Config_P2PKeyBundle.ProtoReflect.Descriptor instead. -func (*OCR2Config_P2PKeyBundle) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{4, 0} -} - -func (x *OCR2Config_P2PKeyBundle) GetPeerId() string { - if x != nil { - return x.PeerId - } - return "" -} - -func (x *OCR2Config_P2PKeyBundle) GetPublicKey() string { - if x != nil { - return x.PublicKey - } - return "" -} - -type OCR2Config_OCRKeyBundle struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - BundleId string `protobuf:"bytes,1,opt,name=bundle_id,json=bundleId,proto3" json:"bundle_id,omitempty"` - ConfigPublicKey string `protobuf:"bytes,2,opt,name=config_public_key,json=configPublicKey,proto3" json:"config_public_key,omitempty"` - OffchainPublicKey string `protobuf:"bytes,3,opt,name=offchain_public_key,json=offchainPublicKey,proto3" json:"offchain_public_key,omitempty"` - OnchainSigningAddress string `protobuf:"bytes,4,opt,name=onchain_signing_address,json=onchainSigningAddress,proto3" json:"onchain_signing_address,omitempty"` -} - -func (x *OCR2Config_OCRKeyBundle) Reset() { - *x = OCR2Config_OCRKeyBundle{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[25] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *OCR2Config_OCRKeyBundle) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OCR2Config_OCRKeyBundle) ProtoMessage() {} - -func (x *OCR2Config_OCRKeyBundle) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[25] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use OCR2Config_OCRKeyBundle.ProtoReflect.Descriptor instead. -func (*OCR2Config_OCRKeyBundle) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{4, 1} -} - -func (x *OCR2Config_OCRKeyBundle) GetBundleId() string { - if x != nil { - return x.BundleId - } - return "" -} - -func (x *OCR2Config_OCRKeyBundle) GetConfigPublicKey() string { - if x != nil { - return x.ConfigPublicKey - } - return "" -} - -func (x *OCR2Config_OCRKeyBundle) GetOffchainPublicKey() string { - if x != nil { - return x.OffchainPublicKey - } - return "" -} - -func (x *OCR2Config_OCRKeyBundle) GetOnchainSigningAddress() string { - if x != nil { - return x.OnchainSigningAddress - } - return "" -} - -type OCR2Config_Plugins struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Commit bool `protobuf:"varint,1,opt,name=commit,proto3" json:"commit,omitempty"` - Execute bool `protobuf:"varint,2,opt,name=execute,proto3" json:"execute,omitempty"` - Median bool `protobuf:"varint,3,opt,name=median,proto3" json:"median,omitempty"` - Mercury bool `protobuf:"varint,4,opt,name=mercury,proto3" json:"mercury,omitempty"` - Rebalancer bool `protobuf:"varint,5,opt,name=rebalancer,proto3" json:"rebalancer,omitempty"` -} - -func (x *OCR2Config_Plugins) Reset() { - *x = OCR2Config_Plugins{} - if protoimpl.UnsafeEnabled { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[26] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *OCR2Config_Plugins) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OCR2Config_Plugins) ProtoMessage() {} - -func (x *OCR2Config_Plugins) ProtoReflect() protoreflect.Message { - mi := &file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[26] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use OCR2Config_Plugins.ProtoReflect.Descriptor instead. -func (*OCR2Config_Plugins) Descriptor() ([]byte, []int) { - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP(), []int{4, 2} -} - -func (x *OCR2Config_Plugins) GetCommit() bool { - if x != nil { - return x.Commit - } - return false -} - -func (x *OCR2Config_Plugins) GetExecute() bool { - if x != nil { - return x.Execute - } - return false -} - -func (x *OCR2Config_Plugins) GetMedian() bool { - if x != nil { - return x.Median - } - return false -} - -func (x *OCR2Config_Plugins) GetMercury() bool { - if x != nil { - return x.Mercury - } - return false -} - -func (x *OCR2Config_Plugins) GetRebalancer() bool { - if x != nil { - return x.Rebalancer - } - return false -} - -var File_orchestrator_feedsmanager_feedsmanager_proto protoreflect.FileDescriptor - -var file_orchestrator_feedsmanager_feedsmanager_proto_rawDesc = []byte{ - 0x0a, 0x2c, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x66, - 0x65, 0x65, 0x64, 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, 0x66, 0x65, 0x65, 0x64, - 0x73, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, - 0x63, 0x66, 0x6d, 0x22, 0x3b, 0x0a, 0x05, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, 0x0a, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x63, 0x66, 0x6d, - 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x22, 0x6d, 0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2d, 0x0a, 0x0a, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x0e, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, - 0x09, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, - 0x2d, 0x0a, 0x11, 0x46, 0x6c, 0x75, 0x78, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xf9, - 0x03, 0x0a, 0x0a, 0x4f, 0x43, 0x52, 0x31, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, - 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, 0x62, 0x6f, - 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, - 0x73, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x12, 0x42, 0x0a, 0x0e, 0x70, 0x32, - 0x70, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x4f, 0x43, 0x52, 0x31, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x2e, 0x50, 0x32, 0x50, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, - 0x52, 0x0c, 0x70, 0x32, 0x70, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x42, - 0x0a, 0x0e, 0x6f, 0x63, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x4f, 0x43, 0x52, - 0x31, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x4f, 0x43, 0x52, 0x4b, 0x65, 0x79, 0x42, 0x75, - 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x0c, 0x6f, 0x63, 0x72, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, - 0x6c, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x61, 0x64, 0x64, 0x72, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x61, 0x64, 0x64, 0x72, - 0x1a, 0x46, 0x0a, 0x0c, 0x50, 0x32, 0x50, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, - 0x12, 0x17, 0x0a, 0x07, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x1a, 0xbf, 0x01, 0x0a, 0x0c, 0x4f, 0x43, 0x52, - 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x75, 0x6e, - 0x64, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x75, - 0x6e, 0x64, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, - 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x13, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x70, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x11, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, - 0x65, 0x79, 0x12, 0x36, 0x0a, 0x17, 0x6f, 0x6e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x69, - 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x15, 0x6f, 0x6e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x53, 0x69, 0x67, 0x6e, - 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x84, 0x06, 0x0a, 0x0a, 0x4f, - 0x43, 0x52, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, - 0x72, 0x61, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x42, 0x6f, 0x6f, - 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x12, 0x42, 0x0a, 0x0e, 0x70, 0x32, 0x70, 0x5f, 0x6b, 0x65, - 0x79, 0x5f, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, - 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x4f, 0x43, 0x52, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, - 0x50, 0x32, 0x50, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x0c, 0x70, 0x32, - 0x70, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x6f, 0x63, - 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x4f, 0x43, 0x52, 0x32, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x2e, 0x4f, 0x43, 0x52, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, - 0x52, 0x0c, 0x6f, 0x63, 0x72, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x1c, - 0x0a, 0x09, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x61, 0x64, 0x64, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x61, 0x64, 0x64, 0x72, 0x12, 0x31, 0x0a, 0x07, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x63, 0x66, 0x6d, 0x2e, 0x4f, 0x43, 0x52, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x50, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x52, 0x07, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x12, - 0x30, 0x0a, 0x11, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x10, 0x66, 0x6f, - 0x72, 0x77, 0x61, 0x72, 0x64, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x88, 0x01, - 0x01, 0x1a, 0x46, 0x0a, 0x0c, 0x50, 0x32, 0x50, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, - 0x65, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x1a, 0xbf, 0x01, 0x0a, 0x0c, 0x4f, 0x43, - 0x52, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x75, - 0x6e, 0x64, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, - 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x4b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x13, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, - 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x11, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x4b, 0x65, 0x79, 0x12, 0x36, 0x0a, 0x17, 0x6f, 0x6e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x73, - 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x6f, 0x6e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x53, 0x69, 0x67, - 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x1a, 0x8d, 0x01, 0x0a, 0x07, - 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, - 0x18, 0x0a, 0x07, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x07, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x64, - 0x69, 0x61, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6d, 0x65, 0x64, 0x69, 0x61, - 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x72, 0x63, 0x75, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x6d, 0x65, 0x72, 0x63, 0x75, 0x72, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x72, - 0x65, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0a, 0x72, 0x65, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x42, 0x14, 0x0a, 0x12, 0x5f, - 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x22, 0x8a, 0x03, 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x12, 0x20, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0a, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x05, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x23, 0x0a, 0x0d, - 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x12, 0x46, 0x0a, 0x13, 0x66, 0x6c, 0x75, 0x78, 0x5f, 0x6d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, - 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, - 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x46, 0x6c, 0x75, 0x78, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x66, 0x6c, 0x75, 0x78, 0x4d, 0x6f, 0x6e, 0x69, - 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x30, 0x0a, 0x0b, 0x6f, 0x63, 0x72, - 0x31, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, - 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x4f, 0x43, 0x52, 0x31, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, - 0x0a, 0x6f, 0x63, 0x72, 0x31, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x30, 0x0a, 0x0b, 0x6f, - 0x63, 0x72, 0x32, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0f, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x4f, 0x43, 0x52, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x52, 0x0a, 0x6f, 0x63, 0x72, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x40, 0x0a, - 0x1a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x09, 0x48, 0x00, 0x52, 0x17, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x88, 0x01, 0x01, 0x42, - 0x1d, 0x0a, 0x1b, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x22, 0x9f, - 0x03, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x09, 0x6a, 0x6f, 0x62, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x0c, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x4a, 0x6f, - 0x62, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x6a, 0x6f, 0x62, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, - 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x69, 0x73, 0x5f, 0x62, 0x6f, - 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0f, 0x69, 0x73, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x50, - 0x65, 0x65, 0x72, 0x12, 0x2f, 0x0a, 0x13, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, - 0x5f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x61, 0x64, 0x64, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x12, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x4d, 0x75, 0x6c, 0x74, 0x69, - 0x61, 0x64, 0x64, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1b, - 0x0a, 0x09, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, - 0x03, 0x52, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x73, 0x12, 0x28, 0x0a, 0x08, 0x61, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, - 0x63, 0x66, 0x6d, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x08, 0x61, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x22, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x18, - 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x43, 0x68, 0x61, 0x69, - 0x6e, 0x52, 0x06, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x0d, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, - 0x22, 0x14, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, 0x0a, 0x12, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, - 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, - 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, - 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x15, 0x0a, 0x13, 0x41, 0x70, - 0x70, 0x72, 0x6f, 0x76, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x14, 0x0a, 0x12, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x15, 0x0a, 0x13, 0x48, 0x65, 0x61, 0x6c, 0x74, - 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, - 0x0a, 0x12, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x22, 0x15, 0x0a, 0x13, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4a, 0x6f, - 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x43, 0x0a, 0x13, 0x43, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x75, 0x75, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x16, - 0x0a, 0x14, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x71, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, - 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, - 0x70, 0x65, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, - 0x1e, 0x0a, 0x0a, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x61, 0x64, 0x64, 0x72, 0x73, 0x12, - 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x24, 0x0a, 0x12, 0x50, 0x72, 0x6f, - 0x70, 0x6f, 0x73, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, - 0x22, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x22, 0x23, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x22, 0x0a, 0x10, 0x52, 0x65, 0x76, 0x6f, - 0x6b, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x23, 0x0a, 0x11, - 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, - 0x64, 0x2a, 0x63, 0x0a, 0x07, 0x4a, 0x6f, 0x62, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, - 0x4a, 0x4f, 0x42, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, - 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x4a, 0x4f, 0x42, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x46, 0x4c, 0x55, 0x58, 0x5f, 0x4d, 0x4f, 0x4e, 0x49, 0x54, 0x4f, 0x52, 0x10, - 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4a, 0x4f, 0x42, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4f, 0x43, - 0x52, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x4a, 0x4f, 0x42, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x4f, 0x43, 0x52, 0x32, 0x10, 0x03, 0x2a, 0x81, 0x01, 0x0a, 0x09, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, - 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, - 0x56, 0x4d, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x53, 0x4f, 0x4c, 0x41, 0x4e, 0x41, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, 0x43, - 0x48, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x4b, 0x4e, - 0x45, 0x54, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x41, 0x50, 0x54, 0x4f, 0x53, 0x10, 0x04, 0x32, 0xd8, 0x02, 0x0a, 0x0c, 0x46, - 0x65, 0x65, 0x64, 0x73, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x40, 0x0a, 0x0b, 0x41, - 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x17, 0x2e, 0x63, 0x66, 0x6d, - 0x2e, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, - 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, - 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x17, 0x2e, 0x63, - 0x66, 0x6d, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x48, 0x65, 0x61, 0x6c, - 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x3d, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x2e, - 0x63, 0x66, 0x6d, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, - 0x0a, 0x0b, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x17, 0x2e, - 0x63, 0x66, 0x6d, 0x2e, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x52, 0x65, 0x6a, - 0x65, 0x63, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x43, 0x0a, 0x0c, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, - 0x12, 0x18, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x65, 0x64, - 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x63, 0x66, 0x6d, - 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xc4, 0x01, 0x0a, 0x0b, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3d, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, - 0x4a, 0x6f, 0x62, 0x12, 0x16, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, - 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x63, 0x66, - 0x6d, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x09, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, - 0x62, 0x12, 0x15, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, - 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x3a, 0x0a, 0x09, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x15, 0x2e, - 0x63, 0x66, 0x6d, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, - 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x48, 0x5a, 0x46, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, 0x72, 0x74, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6b, 0x69, 0x74, 0x2f, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x2d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x6f, 0x72, 0x63, - 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x66, 0x65, 0x65, 0x64, 0x73, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_orchestrator_feedsmanager_feedsmanager_proto_rawDescOnce sync.Once - file_orchestrator_feedsmanager_feedsmanager_proto_rawDescData = file_orchestrator_feedsmanager_feedsmanager_proto_rawDesc -) - -func file_orchestrator_feedsmanager_feedsmanager_proto_rawDescGZIP() []byte { - file_orchestrator_feedsmanager_feedsmanager_proto_rawDescOnce.Do(func() { - file_orchestrator_feedsmanager_feedsmanager_proto_rawDescData = protoimpl.X.CompressGZIP(file_orchestrator_feedsmanager_feedsmanager_proto_rawDescData) - }) - return file_orchestrator_feedsmanager_feedsmanager_proto_rawDescData -} - -var file_orchestrator_feedsmanager_feedsmanager_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes = make([]protoimpl.MessageInfo, 27) -var file_orchestrator_feedsmanager_feedsmanager_proto_goTypes = []any{ - (JobType)(0), // 0: cfm.JobType - (ChainType)(0), // 1: cfm.ChainType - (*Chain)(nil), // 2: cfm.Chain - (*Account)(nil), // 3: cfm.Account - (*FluxMonitorConfig)(nil), // 4: cfm.FluxMonitorConfig - (*OCR1Config)(nil), // 5: cfm.OCR1Config - (*OCR2Config)(nil), // 6: cfm.OCR2Config - (*ChainConfig)(nil), // 7: cfm.ChainConfig - (*UpdateNodeRequest)(nil), // 8: cfm.UpdateNodeRequest - (*UpdateNodeResponse)(nil), // 9: cfm.UpdateNodeResponse - (*ApprovedJobRequest)(nil), // 10: cfm.ApprovedJobRequest - (*ApprovedJobResponse)(nil), // 11: cfm.ApprovedJobResponse - (*HealthcheckRequest)(nil), // 12: cfm.HealthcheckRequest - (*HealthcheckResponse)(nil), // 13: cfm.HealthcheckResponse - (*RejectedJobRequest)(nil), // 14: cfm.RejectedJobRequest - (*RejectedJobResponse)(nil), // 15: cfm.RejectedJobResponse - (*CancelledJobRequest)(nil), // 16: cfm.CancelledJobRequest - (*CancelledJobResponse)(nil), // 17: cfm.CancelledJobResponse - (*ProposeJobRequest)(nil), // 18: cfm.ProposeJobRequest - (*ProposeJobResponse)(nil), // 19: cfm.ProposeJobResponse - (*DeleteJobRequest)(nil), // 20: cfm.DeleteJobRequest - (*DeleteJobResponse)(nil), // 21: cfm.DeleteJobResponse - (*RevokeJobRequest)(nil), // 22: cfm.RevokeJobRequest - (*RevokeJobResponse)(nil), // 23: cfm.RevokeJobResponse - (*OCR1Config_P2PKeyBundle)(nil), // 24: cfm.OCR1Config.P2PKeyBundle - (*OCR1Config_OCRKeyBundle)(nil), // 25: cfm.OCR1Config.OCRKeyBundle - (*OCR2Config_P2PKeyBundle)(nil), // 26: cfm.OCR2Config.P2PKeyBundle - (*OCR2Config_OCRKeyBundle)(nil), // 27: cfm.OCR2Config.OCRKeyBundle - (*OCR2Config_Plugins)(nil), // 28: cfm.OCR2Config.Plugins -} -var file_orchestrator_feedsmanager_feedsmanager_proto_depIdxs = []int32{ - 1, // 0: cfm.Chain.type:type_name -> cfm.ChainType - 1, // 1: cfm.Account.chain_type:type_name -> cfm.ChainType - 24, // 2: cfm.OCR1Config.p2p_key_bundle:type_name -> cfm.OCR1Config.P2PKeyBundle - 25, // 3: cfm.OCR1Config.ocr_key_bundle:type_name -> cfm.OCR1Config.OCRKeyBundle - 26, // 4: cfm.OCR2Config.p2p_key_bundle:type_name -> cfm.OCR2Config.P2PKeyBundle - 27, // 5: cfm.OCR2Config.ocr_key_bundle:type_name -> cfm.OCR2Config.OCRKeyBundle - 28, // 6: cfm.OCR2Config.plugins:type_name -> cfm.OCR2Config.Plugins - 2, // 7: cfm.ChainConfig.chain:type_name -> cfm.Chain - 4, // 8: cfm.ChainConfig.flux_monitor_config:type_name -> cfm.FluxMonitorConfig - 5, // 9: cfm.ChainConfig.ocr1_config:type_name -> cfm.OCR1Config - 6, // 10: cfm.ChainConfig.ocr2_config:type_name -> cfm.OCR2Config - 0, // 11: cfm.UpdateNodeRequest.job_types:type_name -> cfm.JobType - 3, // 12: cfm.UpdateNodeRequest.accounts:type_name -> cfm.Account - 2, // 13: cfm.UpdateNodeRequest.chains:type_name -> cfm.Chain - 7, // 14: cfm.UpdateNodeRequest.chain_configs:type_name -> cfm.ChainConfig - 10, // 15: cfm.FeedsManager.ApprovedJob:input_type -> cfm.ApprovedJobRequest - 12, // 16: cfm.FeedsManager.Healthcheck:input_type -> cfm.HealthcheckRequest - 8, // 17: cfm.FeedsManager.UpdateNode:input_type -> cfm.UpdateNodeRequest - 14, // 18: cfm.FeedsManager.RejectedJob:input_type -> cfm.RejectedJobRequest - 16, // 19: cfm.FeedsManager.CancelledJob:input_type -> cfm.CancelledJobRequest - 18, // 20: cfm.NodeService.ProposeJob:input_type -> cfm.ProposeJobRequest - 20, // 21: cfm.NodeService.DeleteJob:input_type -> cfm.DeleteJobRequest - 22, // 22: cfm.NodeService.RevokeJob:input_type -> cfm.RevokeJobRequest - 11, // 23: cfm.FeedsManager.ApprovedJob:output_type -> cfm.ApprovedJobResponse - 13, // 24: cfm.FeedsManager.Healthcheck:output_type -> cfm.HealthcheckResponse - 9, // 25: cfm.FeedsManager.UpdateNode:output_type -> cfm.UpdateNodeResponse - 15, // 26: cfm.FeedsManager.RejectedJob:output_type -> cfm.RejectedJobResponse - 17, // 27: cfm.FeedsManager.CancelledJob:output_type -> cfm.CancelledJobResponse - 19, // 28: cfm.NodeService.ProposeJob:output_type -> cfm.ProposeJobResponse - 21, // 29: cfm.NodeService.DeleteJob:output_type -> cfm.DeleteJobResponse - 23, // 30: cfm.NodeService.RevokeJob:output_type -> cfm.RevokeJobResponse - 23, // [23:31] is the sub-list for method output_type - 15, // [15:23] is the sub-list for method input_type - 15, // [15:15] is the sub-list for extension type_name - 15, // [15:15] is the sub-list for extension extendee - 0, // [0:15] is the sub-list for field type_name -} - -func init() { file_orchestrator_feedsmanager_feedsmanager_proto_init() } -func file_orchestrator_feedsmanager_feedsmanager_proto_init() { - if File_orchestrator_feedsmanager_feedsmanager_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*Chain); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*Account); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[2].Exporter = func(v any, i int) any { - switch v := v.(*FluxMonitorConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[3].Exporter = func(v any, i int) any { - switch v := v.(*OCR1Config); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[4].Exporter = func(v any, i int) any { - switch v := v.(*OCR2Config); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[5].Exporter = func(v any, i int) any { - switch v := v.(*ChainConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[6].Exporter = func(v any, i int) any { - switch v := v.(*UpdateNodeRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[7].Exporter = func(v any, i int) any { - switch v := v.(*UpdateNodeResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[8].Exporter = func(v any, i int) any { - switch v := v.(*ApprovedJobRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[9].Exporter = func(v any, i int) any { - switch v := v.(*ApprovedJobResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[10].Exporter = func(v any, i int) any { - switch v := v.(*HealthcheckRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[11].Exporter = func(v any, i int) any { - switch v := v.(*HealthcheckResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[12].Exporter = func(v any, i int) any { - switch v := v.(*RejectedJobRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[13].Exporter = func(v any, i int) any { - switch v := v.(*RejectedJobResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[14].Exporter = func(v any, i int) any { - switch v := v.(*CancelledJobRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[15].Exporter = func(v any, i int) any { - switch v := v.(*CancelledJobResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[16].Exporter = func(v any, i int) any { - switch v := v.(*ProposeJobRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[17].Exporter = func(v any, i int) any { - switch v := v.(*ProposeJobResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[18].Exporter = func(v any, i int) any { - switch v := v.(*DeleteJobRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[19].Exporter = func(v any, i int) any { - switch v := v.(*DeleteJobResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[20].Exporter = func(v any, i int) any { - switch v := v.(*RevokeJobRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[21].Exporter = func(v any, i int) any { - switch v := v.(*RevokeJobResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[22].Exporter = func(v any, i int) any { - switch v := v.(*OCR1Config_P2PKeyBundle); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[23].Exporter = func(v any, i int) any { - switch v := v.(*OCR1Config_OCRKeyBundle); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[24].Exporter = func(v any, i int) any { - switch v := v.(*OCR2Config_P2PKeyBundle); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[25].Exporter = func(v any, i int) any { - switch v := v.(*OCR2Config_OCRKeyBundle); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[26].Exporter = func(v any, i int) any { - switch v := v.(*OCR2Config_Plugins); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[4].OneofWrappers = []any{} - file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes[5].OneofWrappers = []any{} - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_orchestrator_feedsmanager_feedsmanager_proto_rawDesc, - NumEnums: 2, - NumMessages: 27, - NumExtensions: 0, - NumServices: 2, - }, - GoTypes: file_orchestrator_feedsmanager_feedsmanager_proto_goTypes, - DependencyIndexes: file_orchestrator_feedsmanager_feedsmanager_proto_depIdxs, - EnumInfos: file_orchestrator_feedsmanager_feedsmanager_proto_enumTypes, - MessageInfos: file_orchestrator_feedsmanager_feedsmanager_proto_msgTypes, - }.Build() - File_orchestrator_feedsmanager_feedsmanager_proto = out.File - file_orchestrator_feedsmanager_feedsmanager_proto_rawDesc = nil - file_orchestrator_feedsmanager_feedsmanager_proto_goTypes = nil - file_orchestrator_feedsmanager_feedsmanager_proto_depIdxs = nil -} diff --git a/core/services/feeds/proto/feedsmanager_wsrpc.pb.go b/core/services/feeds/proto/feedsmanager_wsrpc.pb.go deleted file mode 100644 index f2375448549..00000000000 --- a/core/services/feeds/proto/feedsmanager_wsrpc.pb.go +++ /dev/null @@ -1,261 +0,0 @@ -// Code generated by protoc-gen-go-wsrpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-wsrpc v0.0.1 -// - protoc v5.28.1 - -package feedsmanager - -import ( - context "context" - wsrpc "github.com/smartcontractkit/wsrpc" -) - -// FeedsManagerClient is the client API for FeedsManager service. -type FeedsManagerClient interface { - ApprovedJob(ctx context.Context, in *ApprovedJobRequest) (*ApprovedJobResponse, error) - Healthcheck(ctx context.Context, in *HealthcheckRequest) (*HealthcheckResponse, error) - UpdateNode(ctx context.Context, in *UpdateNodeRequest) (*UpdateNodeResponse, error) - RejectedJob(ctx context.Context, in *RejectedJobRequest) (*RejectedJobResponse, error) - CancelledJob(ctx context.Context, in *CancelledJobRequest) (*CancelledJobResponse, error) -} - -type feedsManagerClient struct { - cc wsrpc.ClientInterface -} - -func NewFeedsManagerClient(cc wsrpc.ClientInterface) FeedsManagerClient { - return &feedsManagerClient{cc} -} - -func (c *feedsManagerClient) ApprovedJob(ctx context.Context, in *ApprovedJobRequest) (*ApprovedJobResponse, error) { - out := new(ApprovedJobResponse) - err := c.cc.Invoke(ctx, "ApprovedJob", in, out) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *feedsManagerClient) Healthcheck(ctx context.Context, in *HealthcheckRequest) (*HealthcheckResponse, error) { - out := new(HealthcheckResponse) - err := c.cc.Invoke(ctx, "Healthcheck", in, out) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *feedsManagerClient) UpdateNode(ctx context.Context, in *UpdateNodeRequest) (*UpdateNodeResponse, error) { - out := new(UpdateNodeResponse) - err := c.cc.Invoke(ctx, "UpdateNode", in, out) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *feedsManagerClient) RejectedJob(ctx context.Context, in *RejectedJobRequest) (*RejectedJobResponse, error) { - out := new(RejectedJobResponse) - err := c.cc.Invoke(ctx, "RejectedJob", in, out) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *feedsManagerClient) CancelledJob(ctx context.Context, in *CancelledJobRequest) (*CancelledJobResponse, error) { - out := new(CancelledJobResponse) - err := c.cc.Invoke(ctx, "CancelledJob", in, out) - if err != nil { - return nil, err - } - return out, nil -} - -// FeedsManagerServer is the server API for FeedsManager service. -type FeedsManagerServer interface { - ApprovedJob(context.Context, *ApprovedJobRequest) (*ApprovedJobResponse, error) - Healthcheck(context.Context, *HealthcheckRequest) (*HealthcheckResponse, error) - UpdateNode(context.Context, *UpdateNodeRequest) (*UpdateNodeResponse, error) - RejectedJob(context.Context, *RejectedJobRequest) (*RejectedJobResponse, error) - CancelledJob(context.Context, *CancelledJobRequest) (*CancelledJobResponse, error) -} - -func RegisterFeedsManagerServer(s wsrpc.ServiceRegistrar, srv FeedsManagerServer) { - s.RegisterService(&FeedsManager_ServiceDesc, srv) -} - -func _FeedsManager_ApprovedJob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { - in := new(ApprovedJobRequest) - if err := dec(in); err != nil { - return nil, err - } - return srv.(FeedsManagerServer).ApprovedJob(ctx, in) -} - -func _FeedsManager_Healthcheck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { - in := new(HealthcheckRequest) - if err := dec(in); err != nil { - return nil, err - } - return srv.(FeedsManagerServer).Healthcheck(ctx, in) -} - -func _FeedsManager_UpdateNode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { - in := new(UpdateNodeRequest) - if err := dec(in); err != nil { - return nil, err - } - return srv.(FeedsManagerServer).UpdateNode(ctx, in) -} - -func _FeedsManager_RejectedJob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { - in := new(RejectedJobRequest) - if err := dec(in); err != nil { - return nil, err - } - return srv.(FeedsManagerServer).RejectedJob(ctx, in) -} - -func _FeedsManager_CancelledJob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { - in := new(CancelledJobRequest) - if err := dec(in); err != nil { - return nil, err - } - return srv.(FeedsManagerServer).CancelledJob(ctx, in) -} - -// FeedsManager_ServiceDesc is the wsrpc.ServiceDesc for FeedsManager service. -// It's only intended for direct use with wsrpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var FeedsManager_ServiceDesc = wsrpc.ServiceDesc{ - ServiceName: "cfm.FeedsManager", - HandlerType: (*FeedsManagerServer)(nil), - Methods: []wsrpc.MethodDesc{ - { - MethodName: "ApprovedJob", - Handler: _FeedsManager_ApprovedJob_Handler, - }, - { - MethodName: "Healthcheck", - Handler: _FeedsManager_Healthcheck_Handler, - }, - { - MethodName: "UpdateNode", - Handler: _FeedsManager_UpdateNode_Handler, - }, - { - MethodName: "RejectedJob", - Handler: _FeedsManager_RejectedJob_Handler, - }, - { - MethodName: "CancelledJob", - Handler: _FeedsManager_CancelledJob_Handler, - }, - }, -} - -// NodeServiceClient is the client API for NodeService service. -type NodeServiceClient interface { - // ProposeJob is called by the JD to propose a job to the node. - ProposeJob(ctx context.Context, in *ProposeJobRequest) (*ProposeJobResponse, error) - // DeleteJob is called by the JD to delete a job from the node. - DeleteJob(ctx context.Context, in *DeleteJobRequest) (*DeleteJobResponse, error) - // RevokeJob is called by the JD to revoke a job from the node. - RevokeJob(ctx context.Context, in *RevokeJobRequest) (*RevokeJobResponse, error) -} - -type nodeServiceClient struct { - cc wsrpc.ClientInterface -} - -func NewNodeServiceClient(cc wsrpc.ClientInterface) NodeServiceClient { - return &nodeServiceClient{cc} -} - -func (c *nodeServiceClient) ProposeJob(ctx context.Context, in *ProposeJobRequest) (*ProposeJobResponse, error) { - out := new(ProposeJobResponse) - err := c.cc.Invoke(ctx, "ProposeJob", in, out) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *nodeServiceClient) DeleteJob(ctx context.Context, in *DeleteJobRequest) (*DeleteJobResponse, error) { - out := new(DeleteJobResponse) - err := c.cc.Invoke(ctx, "DeleteJob", in, out) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *nodeServiceClient) RevokeJob(ctx context.Context, in *RevokeJobRequest) (*RevokeJobResponse, error) { - out := new(RevokeJobResponse) - err := c.cc.Invoke(ctx, "RevokeJob", in, out) - if err != nil { - return nil, err - } - return out, nil -} - -// NodeServiceServer is the server API for NodeService service. -type NodeServiceServer interface { - // ProposeJob is called by the JD to propose a job to the node. - ProposeJob(context.Context, *ProposeJobRequest) (*ProposeJobResponse, error) - // DeleteJob is called by the JD to delete a job from the node. - DeleteJob(context.Context, *DeleteJobRequest) (*DeleteJobResponse, error) - // RevokeJob is called by the JD to revoke a job from the node. - RevokeJob(context.Context, *RevokeJobRequest) (*RevokeJobResponse, error) -} - -func RegisterNodeServiceServer(s wsrpc.ServiceRegistrar, srv NodeServiceServer) { - s.RegisterService(&NodeService_ServiceDesc, srv) -} - -func _NodeService_ProposeJob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { - in := new(ProposeJobRequest) - if err := dec(in); err != nil { - return nil, err - } - return srv.(NodeServiceServer).ProposeJob(ctx, in) -} - -func _NodeService_DeleteJob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { - in := new(DeleteJobRequest) - if err := dec(in); err != nil { - return nil, err - } - return srv.(NodeServiceServer).DeleteJob(ctx, in) -} - -func _NodeService_RevokeJob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { - in := new(RevokeJobRequest) - if err := dec(in); err != nil { - return nil, err - } - return srv.(NodeServiceServer).RevokeJob(ctx, in) -} - -// NodeService_ServiceDesc is the wsrpc.ServiceDesc for NodeService service. -// It's only intended for direct use with wsrpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var NodeService_ServiceDesc = wsrpc.ServiceDesc{ - ServiceName: "cfm.NodeService", - HandlerType: (*NodeServiceServer)(nil), - Methods: []wsrpc.MethodDesc{ - { - MethodName: "ProposeJob", - Handler: _NodeService_ProposeJob_Handler, - }, - { - MethodName: "DeleteJob", - Handler: _NodeService_DeleteJob_Handler, - }, - { - MethodName: "RevokeJob", - Handler: _NodeService_RevokeJob_Handler, - }, - }, -} diff --git a/core/services/feeds/rpc_handlers.go b/core/services/feeds/rpc_handlers.go index 770d99861e5..a079204534e 100644 --- a/core/services/feeds/rpc_handlers.go +++ b/core/services/feeds/rpc_handlers.go @@ -5,7 +5,7 @@ import ( "github.com/google/uuid" - pb "github.com/smartcontractkit/chainlink/v2/core/services/feeds/proto" + pb "github.com/smartcontractkit/chainlink-protos/orchestrator/feedsmanager" ) // RPCHandlers define handlers for RPC method calls from the Feeds Manager diff --git a/core/services/feeds/rpc_handlers_test.go b/core/services/feeds/rpc_handlers_test.go index 1648c05954d..2ba2fc15816 100644 --- a/core/services/feeds/rpc_handlers_test.go +++ b/core/services/feeds/rpc_handlers_test.go @@ -7,10 +7,10 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/require" + pb "github.com/smartcontractkit/chainlink-protos/orchestrator/feedsmanager" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/feeds" "github.com/smartcontractkit/chainlink/v2/core/services/feeds/mocks" - pb "github.com/smartcontractkit/chainlink/v2/core/services/feeds/proto" ) type TestRPCHandlers struct { diff --git a/core/services/feeds/service.go b/core/services/feeds/service.go index d5c8c1ba22e..7479f10abb5 100644 --- a/core/services/feeds/service.go +++ b/core/services/feeds/service.go @@ -21,11 +21,11 @@ import ( ccip "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/validate" "github.com/smartcontractkit/chainlink/v2/plugins" + pb "github.com/smartcontractkit/chainlink-protos/orchestrator/feedsmanager" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" - pb "github.com/smartcontractkit/chainlink/v2/core/services/feeds/proto" "github.com/smartcontractkit/chainlink/v2/core/services/fluxmonitorv2" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" @@ -1225,9 +1225,9 @@ func (s *service) generateJob(ctx context.Context, spec string) (*job.Job, error // newChainConfigMsg generates a chain config protobuf message. func (s *service) newChainConfigMsg(cfg ChainConfig) (*pb.ChainConfig, error) { - // Only supports EVM Chains - if cfg.ChainType != "EVM" { - return nil, errors.New("unsupported chain type") + protoChainType := ChainTypeToProtoChainType(cfg.ChainType) + if protoChainType == pb.ChainType_CHAIN_TYPE_UNSPECIFIED { + return nil, errors.Errorf("unsupported chain type: %s", cfg.ChainType) } ocr1Cfg, err := s.newOCR1ConfigMsg(cfg.OCR1Config) @@ -1243,7 +1243,7 @@ func (s *service) newChainConfigMsg(cfg ChainConfig) (*pb.ChainConfig, error) { pbChainConfig := pb.ChainConfig{ Chain: &pb.Chain{ Id: cfg.ChainID, - Type: pb.ChainType_CHAIN_TYPE_EVM, + Type: protoChainType, }, AccountAddress: cfg.AccountAddress, AdminAddress: cfg.AdminAddress, diff --git a/core/services/feeds/service_test.go b/core/services/feeds/service_test.go index 88c034657dc..e7472a5ae54 100644 --- a/core/services/feeds/service_test.go +++ b/core/services/feeds/service_test.go @@ -19,6 +19,7 @@ import ( commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + proto "github.com/smartcontractkit/chainlink-protos/orchestrator/feedsmanager" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" @@ -32,7 +33,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/feeds" "github.com/smartcontractkit/chainlink/v2/core/services/feeds/mocks" - proto "github.com/smartcontractkit/chainlink/v2/core/services/feeds/proto" "github.com/smartcontractkit/chainlink/v2/core/services/job" jobmocks "github.com/smartcontractkit/chainlink/v2/core/services/job/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/csakey" @@ -484,57 +484,93 @@ func Test_Service_ListManagersByIDs(t *testing.T) { } func Test_Service_CreateChainConfig(t *testing.T) { - var ( - mgr = feeds.FeedsManager{ID: 1} - nodeVersion = &versioning.NodeVersion{ - Version: "1.0.0", - } - cfg = feeds.ChainConfig{ - FeedsManagerID: mgr.ID, - ChainID: "42", - ChainType: feeds.ChainTypeEVM, - AccountAddress: "0x0000000000000000000000000000000000000000", - AccountAddressPublicKey: null.StringFrom("0x0000000000000000000000000000000000000002"), - AdminAddress: "0x0000000000000000000000000000000000000001", - FluxMonitorConfig: feeds.FluxMonitorConfig{ - Enabled: true, - }, - OCR1Config: feeds.OCR1Config{ - Enabled: false, - }, - OCR2Config: feeds.OCR2ConfigModel{ - Enabled: false, - }, - } + tests := []struct { + name string + chainType feeds.ChainType + expectedID int64 + expectedChainType proto.ChainType + }{ + { + name: "EVM Chain Type", + chainType: feeds.ChainTypeEVM, + expectedID: int64(1), + expectedChainType: proto.ChainType_CHAIN_TYPE_EVM, + }, + { + name: "Solana Chain Type", + chainType: feeds.ChainTypeSolana, + expectedID: int64(1), + expectedChainType: proto.ChainType_CHAIN_TYPE_SOLANA, + }, + { + name: "Starknet Chain Type", + chainType: feeds.ChainTypeStarknet, + expectedID: int64(1), + expectedChainType: proto.ChainType_CHAIN_TYPE_STARKNET, + }, + { + name: "Aptos Chain Type", + chainType: feeds.ChainTypeAptos, + expectedID: int64(1), + expectedChainType: proto.ChainType_CHAIN_TYPE_APTOS, + }, + } - svc = setupTestService(t) - ) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var ( + mgr = feeds.FeedsManager{ID: 1} + nodeVersion = &versioning.NodeVersion{ + Version: "1.0.0", + } + cfg = feeds.ChainConfig{ + FeedsManagerID: mgr.ID, + ChainID: "42", + ChainType: tt.chainType, + AccountAddress: "0x0000000000000000000000000000000000000000", + AccountAddressPublicKey: null.StringFrom("0x0000000000000000000000000000000000000002"), + AdminAddress: "0x0000000000000000000000000000000000000001", + FluxMonitorConfig: feeds.FluxMonitorConfig{ + Enabled: true, + }, + OCR1Config: feeds.OCR1Config{ + Enabled: false, + }, + OCR2Config: feeds.OCR2ConfigModel{ + Enabled: false, + }, + } - svc.orm.On("CreateChainConfig", mock.Anything, cfg).Return(int64(1), nil) - svc.orm.On("GetManager", mock.Anything, mgr.ID).Return(&mgr, nil) - svc.connMgr.On("GetClient", mgr.ID).Return(svc.fmsClient, nil) - svc.orm.On("ListChainConfigsByManagerIDs", mock.Anything, []int64{mgr.ID}).Return([]feeds.ChainConfig{cfg}, nil) - svc.fmsClient.On("UpdateNode", mock.Anything, &proto.UpdateNodeRequest{ - Version: nodeVersion.Version, - ChainConfigs: []*proto.ChainConfig{ - { - Chain: &proto.Chain{ - Id: cfg.ChainID, - Type: proto.ChainType_CHAIN_TYPE_EVM, + svc = setupTestService(t) + ) + + svc.orm.On("CreateChainConfig", mock.Anything, cfg).Return(int64(1), nil) + svc.orm.On("GetManager", mock.Anything, mgr.ID).Return(&mgr, nil) + svc.connMgr.On("GetClient", mgr.ID).Return(svc.fmsClient, nil) + svc.orm.On("ListChainConfigsByManagerIDs", mock.Anything, []int64{mgr.ID}).Return([]feeds.ChainConfig{cfg}, nil) + svc.fmsClient.On("UpdateNode", mock.Anything, &proto.UpdateNodeRequest{ + Version: nodeVersion.Version, + ChainConfigs: []*proto.ChainConfig{ + { + Chain: &proto.Chain{ + Id: cfg.ChainID, + Type: tt.expectedChainType, + }, + AccountAddress: cfg.AccountAddress, + AccountAddressPublicKey: &cfg.AccountAddressPublicKey.String, + AdminAddress: cfg.AdminAddress, + FluxMonitorConfig: &proto.FluxMonitorConfig{Enabled: true}, + Ocr1Config: &proto.OCR1Config{Enabled: false}, + Ocr2Config: &proto.OCR2Config{Enabled: false}, + }, }, - AccountAddress: cfg.AccountAddress, - AccountAddressPublicKey: &cfg.AccountAddressPublicKey.String, - AdminAddress: cfg.AdminAddress, - FluxMonitorConfig: &proto.FluxMonitorConfig{Enabled: true}, - Ocr1Config: &proto.OCR1Config{Enabled: false}, - Ocr2Config: &proto.OCR2Config{Enabled: false}, - }, - }, - }).Return(&proto.UpdateNodeResponse{}, nil) + }).Return(&proto.UpdateNodeResponse{}, nil) - actual, err := svc.CreateChainConfig(testutils.Context(t), cfg) - require.NoError(t, err) - assert.Equal(t, int64(1), actual) + actual, err := svc.CreateChainConfig(testutils.Context(t), cfg) + require.NoError(t, err) + assert.Equal(t, tt.expectedID, actual) + }) + } } func Test_Service_CreateChainConfig_InvalidAdminAddress(t *testing.T) { @@ -608,51 +644,82 @@ func Test_Service_ListChainConfigsByManagerIDs(t *testing.T) { } func Test_Service_UpdateChainConfig(t *testing.T) { - var ( - mgr = feeds.FeedsManager{ID: 1} - nodeVersion = &versioning.NodeVersion{ - Version: "1.0.0", - } - cfg = feeds.ChainConfig{ - FeedsManagerID: mgr.ID, - ChainID: "42", - ChainType: feeds.ChainTypeEVM, - AccountAddress: "0x0000000000000000000000000000000000000000", - AccountAddressPublicKey: null.StringFrom("0x0000000000000000000000000000000000000002"), - AdminAddress: "0x0000000000000000000000000000000000000001", - FluxMonitorConfig: feeds.FluxMonitorConfig{Enabled: false}, - OCR1Config: feeds.OCR1Config{Enabled: false}, - OCR2Config: feeds.OCR2ConfigModel{Enabled: false}, - } + tests := []struct { + name string + chainType feeds.ChainType + expectedChainType proto.ChainType + }{ + { + name: "EVM Chain Type", + chainType: feeds.ChainTypeEVM, + expectedChainType: proto.ChainType_CHAIN_TYPE_EVM, + }, + { + name: "Solana Chain Type", + chainType: feeds.ChainTypeSolana, + expectedChainType: proto.ChainType_CHAIN_TYPE_SOLANA, + }, + { + name: "Starknet Chain Type", + chainType: feeds.ChainTypeStarknet, + expectedChainType: proto.ChainType_CHAIN_TYPE_STARKNET, + }, + { + name: "Aptos Chain Type", + chainType: feeds.ChainTypeAptos, + expectedChainType: proto.ChainType_CHAIN_TYPE_APTOS, + }, + } - svc = setupTestService(t) - ) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var ( + mgr = feeds.FeedsManager{ID: 1} + nodeVersion = &versioning.NodeVersion{ + Version: "1.0.0", + } + cfg = feeds.ChainConfig{ + FeedsManagerID: mgr.ID, + ChainID: "42", + ChainType: tt.chainType, + AccountAddress: "0x0000000000000000000000000000000000000000", + AccountAddressPublicKey: null.StringFrom("0x0000000000000000000000000000000000000002"), + AdminAddress: "0x0000000000000000000000000000000000000001", + FluxMonitorConfig: feeds.FluxMonitorConfig{Enabled: false}, + OCR1Config: feeds.OCR1Config{Enabled: false}, + OCR2Config: feeds.OCR2ConfigModel{Enabled: false}, + } - svc.orm.On("UpdateChainConfig", mock.Anything, cfg).Return(int64(1), nil) - svc.orm.On("GetChainConfig", mock.Anything, cfg.ID).Return(&cfg, nil) - svc.connMgr.On("GetClient", mgr.ID).Return(svc.fmsClient, nil) - svc.orm.On("ListChainConfigsByManagerIDs", mock.Anything, []int64{mgr.ID}).Return([]feeds.ChainConfig{cfg}, nil) - svc.fmsClient.On("UpdateNode", mock.Anything, &proto.UpdateNodeRequest{ - Version: nodeVersion.Version, - ChainConfigs: []*proto.ChainConfig{ - { - Chain: &proto.Chain{ - Id: cfg.ChainID, - Type: proto.ChainType_CHAIN_TYPE_EVM, + svc = setupTestService(t) + ) + + svc.orm.On("UpdateChainConfig", mock.Anything, cfg).Return(int64(1), nil) + svc.orm.On("GetChainConfig", mock.Anything, cfg.ID).Return(&cfg, nil) + svc.connMgr.On("GetClient", mgr.ID).Return(svc.fmsClient, nil) + svc.orm.On("ListChainConfigsByManagerIDs", mock.Anything, []int64{mgr.ID}).Return([]feeds.ChainConfig{cfg}, nil) + svc.fmsClient.On("UpdateNode", mock.Anything, &proto.UpdateNodeRequest{ + Version: nodeVersion.Version, + ChainConfigs: []*proto.ChainConfig{ + { + Chain: &proto.Chain{ + Id: cfg.ChainID, + Type: tt.expectedChainType, + }, + AccountAddress: cfg.AccountAddress, + AdminAddress: cfg.AdminAddress, + AccountAddressPublicKey: &cfg.AccountAddressPublicKey.String, + FluxMonitorConfig: &proto.FluxMonitorConfig{Enabled: false}, + Ocr1Config: &proto.OCR1Config{Enabled: false}, + Ocr2Config: &proto.OCR2Config{Enabled: false}, + }, }, - AccountAddress: cfg.AccountAddress, - AdminAddress: cfg.AdminAddress, - AccountAddressPublicKey: &cfg.AccountAddressPublicKey.String, - FluxMonitorConfig: &proto.FluxMonitorConfig{Enabled: false}, - Ocr1Config: &proto.OCR1Config{Enabled: false}, - Ocr2Config: &proto.OCR2Config{Enabled: false}, - }, - }, - }).Return(&proto.UpdateNodeResponse{}, nil) + }).Return(&proto.UpdateNodeResponse{}, nil) - actual, err := svc.UpdateChainConfig(testutils.Context(t), cfg) - require.NoError(t, err) - assert.Equal(t, int64(1), actual) + actual, err := svc.UpdateChainConfig(testutils.Context(t), cfg) + require.NoError(t, err) + assert.Equal(t, int64(1), actual) + }) + } } func Test_Service_UpdateChainConfig_InvalidAdminAddress(t *testing.T) { @@ -1492,102 +1559,133 @@ answer1 [type=median index=0]; } func Test_Service_SyncNodeInfo(t *testing.T) { - p2pKey := keystest.NewP2PKeyV2(t) - - ocrKey, err := ocrkey.NewV2() - require.NoError(t, err) - - var ( - multiaddr = "/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju" - mgr = &feeds.FeedsManager{ID: 1} - forwarderAddr = "0x0002" - ccfg = feeds.ChainConfig{ - ID: 100, - FeedsManagerID: mgr.ID, - ChainID: "42", - ChainType: feeds.ChainTypeEVM, - AccountAddress: "0x0000", - AdminAddress: "0x0001", - FluxMonitorConfig: feeds.FluxMonitorConfig{ - Enabled: true, - }, - OCR1Config: feeds.OCR1Config{ - Enabled: true, - IsBootstrap: false, - P2PPeerID: null.StringFrom(p2pKey.PeerID().String()), - KeyBundleID: null.StringFrom(ocrKey.GetID()), - }, - OCR2Config: feeds.OCR2ConfigModel{ - Enabled: true, - IsBootstrap: true, - Multiaddr: null.StringFrom(multiaddr), - ForwarderAddress: null.StringFrom(forwarderAddr), - Plugins: feeds.Plugins{ - Commit: true, - Execute: true, - Median: false, - Mercury: true, - Rebalancer: true, - }, - }, - } - chainConfigs = []feeds.ChainConfig{ccfg} - nodeVersion = &versioning.NodeVersion{Version: "1.0.0"} - ) - - svc := setupTestService(t) + tests := []struct { + name string + chainType feeds.ChainType + protoType proto.ChainType + }{ + { + name: "EVM Chain Type", + chainType: feeds.ChainTypeEVM, + protoType: proto.ChainType_CHAIN_TYPE_EVM, + }, + { + name: "Solana Chain Type", + chainType: feeds.ChainTypeSolana, + protoType: proto.ChainType_CHAIN_TYPE_SOLANA, + }, + { + name: "Starknet Chain Type", + chainType: feeds.ChainTypeStarknet, + protoType: proto.ChainType_CHAIN_TYPE_STARKNET, + }, + { + name: "Aptos Chain Type", + chainType: feeds.ChainTypeAptos, + protoType: proto.ChainType_CHAIN_TYPE_APTOS, + }, + } - svc.connMgr.On("GetClient", mgr.ID).Return(svc.fmsClient, nil) - svc.orm.On("ListChainConfigsByManagerIDs", mock.Anything, []int64{mgr.ID}).Return(chainConfigs, nil) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p2pKey := keystest.NewP2PKeyV2(t) - // OCR1 key fetching - svc.p2pKeystore.On("Get", p2pKey.PeerID()).Return(p2pKey, nil) - svc.ocr1Keystore.On("Get", ocrKey.GetID()).Return(ocrKey, nil) + ocrKey, err := ocrkey.NewV2() + require.NoError(t, err) - svc.fmsClient.On("UpdateNode", mock.Anything, &proto.UpdateNodeRequest{ - Version: nodeVersion.Version, - ChainConfigs: []*proto.ChainConfig{ - { - Chain: &proto.Chain{ - Id: ccfg.ChainID, - Type: proto.ChainType_CHAIN_TYPE_EVM, - }, - AccountAddress: ccfg.AccountAddress, - AdminAddress: ccfg.AdminAddress, - FluxMonitorConfig: &proto.FluxMonitorConfig{Enabled: true}, - Ocr1Config: &proto.OCR1Config{ - Enabled: true, - IsBootstrap: ccfg.OCR1Config.IsBootstrap, - P2PKeyBundle: &proto.OCR1Config_P2PKeyBundle{ - PeerId: p2pKey.PeerID().String(), - PublicKey: p2pKey.PublicKeyHex(), + var ( + multiaddr = "/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju" + mgr = &feeds.FeedsManager{ID: 1} + forwarderAddr = "0x0002" + ccfg = feeds.ChainConfig{ + ID: 100, + FeedsManagerID: mgr.ID, + ChainID: "42", + ChainType: tt.chainType, + AccountAddress: "0x0000", + AdminAddress: "0x0001", + FluxMonitorConfig: feeds.FluxMonitorConfig{ + Enabled: true, }, - OcrKeyBundle: &proto.OCR1Config_OCRKeyBundle{ - BundleId: ocrKey.GetID(), - ConfigPublicKey: ocrkey.ConfigPublicKey(ocrKey.PublicKeyConfig()).String(), - OffchainPublicKey: ocrKey.OffChainSigning.PublicKey().String(), - OnchainSigningAddress: ocrKey.OnChainSigning.Address().String(), + OCR1Config: feeds.OCR1Config{ + Enabled: true, + IsBootstrap: false, + P2PPeerID: null.StringFrom(p2pKey.PeerID().String()), + KeyBundleID: null.StringFrom(ocrKey.GetID()), }, - }, - Ocr2Config: &proto.OCR2Config{ - Enabled: true, - IsBootstrap: ccfg.OCR2Config.IsBootstrap, - Multiaddr: multiaddr, - ForwarderAddress: &forwarderAddr, - Plugins: &proto.OCR2Config_Plugins{ - Commit: ccfg.OCR2Config.Plugins.Commit, - Execute: ccfg.OCR2Config.Plugins.Execute, - Median: ccfg.OCR2Config.Plugins.Median, - Mercury: ccfg.OCR2Config.Plugins.Mercury, - Rebalancer: ccfg.OCR2Config.Plugins.Rebalancer, + OCR2Config: feeds.OCR2ConfigModel{ + Enabled: true, + IsBootstrap: true, + Multiaddr: null.StringFrom(multiaddr), + ForwarderAddress: null.StringFrom(forwarderAddr), + Plugins: feeds.Plugins{ + Commit: true, + Execute: true, + Median: false, + Mercury: true, + Rebalancer: true, + }, + }, + } + chainConfigs = []feeds.ChainConfig{ccfg} + nodeVersion = &versioning.NodeVersion{Version: "1.0.0"} + ) + + svc := setupTestService(t) + + svc.connMgr.On("GetClient", mgr.ID).Return(svc.fmsClient, nil) + svc.orm.On("ListChainConfigsByManagerIDs", mock.Anything, []int64{mgr.ID}).Return(chainConfigs, nil) + + // OCR1 key fetching + svc.p2pKeystore.On("Get", p2pKey.PeerID()).Return(p2pKey, nil) + svc.ocr1Keystore.On("Get", ocrKey.GetID()).Return(ocrKey, nil) + + svc.fmsClient.On("UpdateNode", mock.Anything, &proto.UpdateNodeRequest{ + Version: nodeVersion.Version, + ChainConfigs: []*proto.ChainConfig{ + { + Chain: &proto.Chain{ + Id: ccfg.ChainID, + Type: tt.protoType, + }, + AccountAddress: ccfg.AccountAddress, + AdminAddress: ccfg.AdminAddress, + FluxMonitorConfig: &proto.FluxMonitorConfig{Enabled: true}, + Ocr1Config: &proto.OCR1Config{ + Enabled: true, + IsBootstrap: ccfg.OCR1Config.IsBootstrap, + P2PKeyBundle: &proto.OCR1Config_P2PKeyBundle{ + PeerId: p2pKey.PeerID().String(), + PublicKey: p2pKey.PublicKeyHex(), + }, + OcrKeyBundle: &proto.OCR1Config_OCRKeyBundle{ + BundleId: ocrKey.GetID(), + ConfigPublicKey: ocrkey.ConfigPublicKey(ocrKey.PublicKeyConfig()).String(), + OffchainPublicKey: ocrKey.OffChainSigning.PublicKey().String(), + OnchainSigningAddress: ocrKey.OnChainSigning.Address().String(), + }, + }, + Ocr2Config: &proto.OCR2Config{ + Enabled: true, + IsBootstrap: ccfg.OCR2Config.IsBootstrap, + Multiaddr: multiaddr, + ForwarderAddress: &forwarderAddr, + Plugins: &proto.OCR2Config_Plugins{ + Commit: ccfg.OCR2Config.Plugins.Commit, + Execute: ccfg.OCR2Config.Plugins.Execute, + Median: ccfg.OCR2Config.Plugins.Median, + Mercury: ccfg.OCR2Config.Plugins.Mercury, + Rebalancer: ccfg.OCR2Config.Plugins.Rebalancer, + }, + }, }, }, - }, - }, - }).Return(&proto.UpdateNodeResponse{}, nil) + }).Return(&proto.UpdateNodeResponse{}, nil) - err = svc.SyncNodeInfo(testutils.Context(t), mgr.ID) - require.NoError(t, err) + err = svc.SyncNodeInfo(testutils.Context(t), mgr.ID) + require.NoError(t, err) + }) + } } func Test_Service_IsJobManaged(t *testing.T) { diff --git a/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go b/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go index d34241a2d08..e45b59b919c 100644 --- a/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go +++ b/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go @@ -39,6 +39,8 @@ import ( cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" coretypes "github.com/smartcontractkit/chainlink-common/pkg/types/core/mocks" + pb "github.com/smartcontractkit/chainlink-protos/orchestrator/feedsmanager" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" v2 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -56,7 +58,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" feeds2 "github.com/smartcontractkit/chainlink/v2/core/services/feeds" feedsMocks "github.com/smartcontractkit/chainlink/v2/core/services/feeds/mocks" - pb "github.com/smartcontractkit/chainlink/v2/core/services/feeds/proto" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" diff --git a/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/chainlink.go b/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/chainlink.go index bff08e86385..2bfa775728d 100644 --- a/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/chainlink.go +++ b/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/chainlink.go @@ -38,6 +38,7 @@ import ( cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" coretypes "github.com/smartcontractkit/chainlink-common/pkg/types/core/mocks" + pb "github.com/smartcontractkit/chainlink-protos/orchestrator/feedsmanager" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" v2 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -54,7 +55,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" feeds2 "github.com/smartcontractkit/chainlink/v2/core/services/feeds" feedsMocks "github.com/smartcontractkit/chainlink/v2/core/services/feeds/mocks" - pb "github.com/smartcontractkit/chainlink/v2/core/services/feeds/proto" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" diff --git a/core/services/ocrcommon/peer_wrapper.go b/core/services/ocrcommon/peer_wrapper.go index 325311352fb..a33afe5f589 100644 --- a/core/services/ocrcommon/peer_wrapper.go +++ b/core/services/ocrcommon/peer_wrapper.go @@ -55,6 +55,9 @@ type ( // OCR2 peer adapter Peer2 *peerAdapterOCR2 + + // PeerGroupFactory can be used to create PeerGroup instances + PeerGroupFactory ocrnetworking.PeerGroupFactory } ) @@ -102,6 +105,9 @@ func (p *SingletonPeerWrapper) Start(context.Context) error { peer.OCR2BinaryNetworkEndpointFactory(), peer.OCR2BootstrapperFactory(), } + + p.PeerGroupFactory = peer.PeerGroupFactory() + p.peerCloser = peer return nil }) diff --git a/core/services/relay/evm/mercury/wsrpc/pb/mercury.pb.go b/core/services/relay/evm/mercury/wsrpc/pb/mercury.pb.go index ab74d426bbe..c3755b36809 100644 --- a/core/services/relay/evm/mercury/wsrpc/pb/mercury.pb.go +++ b/core/services/relay/evm/mercury/wsrpc/pb/mercury.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.34.2 +// protoc-gen-go v1.35.1 // protoc v4.25.1 // source: mercury.proto @@ -31,11 +31,9 @@ type TransmitRequest struct { func (x *TransmitRequest) Reset() { *x = TransmitRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_mercury_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_mercury_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *TransmitRequest) String() string { @@ -46,7 +44,7 @@ func (*TransmitRequest) ProtoMessage() {} func (x *TransmitRequest) ProtoReflect() protoreflect.Message { mi := &file_mercury_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -86,11 +84,9 @@ type TransmitResponse struct { func (x *TransmitResponse) Reset() { *x = TransmitResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_mercury_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_mercury_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *TransmitResponse) String() string { @@ -101,7 +97,7 @@ func (*TransmitResponse) ProtoMessage() {} func (x *TransmitResponse) ProtoReflect() protoreflect.Message { mi := &file_mercury_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -140,11 +136,9 @@ type LatestReportRequest struct { func (x *LatestReportRequest) Reset() { *x = LatestReportRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_mercury_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_mercury_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *LatestReportRequest) String() string { @@ -155,7 +149,7 @@ func (*LatestReportRequest) ProtoMessage() {} func (x *LatestReportRequest) ProtoReflect() protoreflect.Message { mi := &file_mercury_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -188,11 +182,9 @@ type LatestReportResponse struct { func (x *LatestReportResponse) Reset() { *x = LatestReportResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_mercury_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_mercury_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *LatestReportResponse) String() string { @@ -203,7 +195,7 @@ func (*LatestReportResponse) ProtoMessage() {} func (x *LatestReportResponse) ProtoReflect() protoreflect.Message { mi := &file_mercury_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -255,11 +247,9 @@ type Report struct { func (x *Report) Reset() { *x = Report{} - if protoimpl.UnsafeEnabled { - mi := &file_mercury_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_mercury_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Report) String() string { @@ -270,7 +260,7 @@ func (*Report) ProtoMessage() {} func (x *Report) ProtoReflect() protoreflect.Message { mi := &file_mercury_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -402,11 +392,9 @@ type Timestamp struct { func (x *Timestamp) Reset() { *x = Timestamp{} - if protoimpl.UnsafeEnabled { - mi := &file_mercury_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_mercury_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Timestamp) String() string { @@ -417,7 +405,7 @@ func (*Timestamp) ProtoMessage() {} func (x *Timestamp) ProtoReflect() protoreflect.Message { mi := &file_mercury_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -562,80 +550,6 @@ func file_mercury_proto_init() { if File_mercury_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_mercury_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*TransmitRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_mercury_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*TransmitResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_mercury_proto_msgTypes[2].Exporter = func(v any, i int) any { - switch v := v.(*LatestReportRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_mercury_proto_msgTypes[3].Exporter = func(v any, i int) any { - switch v := v.(*LatestReportResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_mercury_proto_msgTypes[4].Exporter = func(v any, i int) any { - switch v := v.(*Report); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_mercury_proto_msgTypes[5].Exporter = func(v any, i int) any { - switch v := v.(*Timestamp); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/core/services/workflows/engine_test.go b/core/services/workflows/engine_test.go index 382662afeb1..13427bd878c 100644 --- a/core/services/workflows/engine_test.go +++ b/core/services/workflows/engine_test.go @@ -1455,3 +1455,47 @@ func TestEngine_WithCustomComputeStep(t *testing.T) { assert.True(t, ok) assert.True(t, res.Outputs.(*values.Map).Underlying["Value"].(*values.Bool).Underlying) } + +func TestEngine_CustomComputePropagatesBreaks(t *testing.T) { + cmd := "core/services/workflows/test/break/cmd" + binary := "test/wasm/break/testmodule.wasm" + + ctx := testutils.Context(t) + log := logger.TestLogger(t) + reg := coreCap.NewRegistry(logger.TestLogger(t)) + + compute := compute.NewAction(log, reg) + require.NoError(t, compute.Start(ctx)) + defer compute.Close() + + trigger := basicTestTrigger(t) + require.NoError(t, reg.Add(ctx, trigger)) + + binaryB := wasmtest.CreateTestBinary(cmd, binary, true, t) + + spec, err := host.GetWorkflowSpec( + &host.ModuleConfig{Logger: log}, + binaryB, + nil, // config + ) + require.NoError(t, err) + eng, testHooks := newTestEngine( + t, + reg, + *spec, + func(c *Config) { + c.Binary = binaryB + c.Config = nil + }, + ) + reg.SetLocalRegistry(testConfigProvider{}) + + servicetest.Run(t, eng) + + eid := getExecutionId(t, eng, testHooks) + + state, err := eng.executionStates.Get(ctx, eid) + require.NoError(t, err) + + assert.Equal(t, state.Status, store.StatusCompletedEarlyExit) +} diff --git a/core/services/workflows/test/break/cmd/main.go b/core/services/workflows/test/break/cmd/main.go new file mode 100644 index 00000000000..498d3b7b906 --- /dev/null +++ b/core/services/workflows/test/break/cmd/main.go @@ -0,0 +1,38 @@ +//go:build wasip1 + +package main + +import ( + "github.com/smartcontractkit/chainlink-common/pkg/workflows/wasm" + + "github.com/smartcontractkit/chainlink-common/pkg/capabilities/cli/cmd/testdata/fixtures/capabilities/basictrigger" + "github.com/smartcontractkit/chainlink-common/pkg/workflows/sdk" +) + +func BuildWorkflow(config []byte) *sdk.WorkflowSpecFactory { + workflow := sdk.NewWorkflowSpecFactory( + sdk.NewWorkflowParams{ + Name: "tester", + Owner: "ryan", + }, + ) + + triggerCfg := basictrigger.TriggerConfig{Name: "trigger", Number: 100} + trigger := triggerCfg.New(workflow) + + sdk.Compute1( + workflow, + "compute", + sdk.Compute1Inputs[basictrigger.TriggerOutputs]{Arg0: trigger}, + func(_ sdk.Runtime, outputs basictrigger.TriggerOutputs) (bool, error) { + return false, sdk.BreakErr + }) + + return workflow +} + +func main() { + runner := wasm.NewRunner() + workflow := BuildWorkflow(runner.Config()) + runner.Run(workflow) +} diff --git a/core/web/assets/index.html b/core/web/assets/index.html index d5411542a95..0dae8ddf4f0 100644 --- a/core/web/assets/index.html +++ b/core/web/assets/index.html @@ -1 +1 @@ -Operator UIChainlink
\ No newline at end of file +Operator UIChainlink
\ No newline at end of file diff --git a/core/web/assets/index.html.gz b/core/web/assets/index.html.gz index 4f63d9ebf44..bf45041a3e2 100644 Binary files a/core/web/assets/index.html.gz and b/core/web/assets/index.html.gz differ diff --git a/core/web/assets/main.84f90f8fc23465846aa7.js b/core/web/assets/main.d9fae2bacda0e72ac5ae.js similarity index 91% rename from core/web/assets/main.84f90f8fc23465846aa7.js rename to core/web/assets/main.d9fae2bacda0e72ac5ae.js index f9770459bef..9f536101c7f 100644 --- a/core/web/assets/main.84f90f8fc23465846aa7.js +++ b/core/web/assets/main.d9fae2bacda0e72ac5ae.js @@ -168,7 +168,7 @@ object-assign * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */ Object.defineProperty(t,"__esModule",{value:!0}),"undefined"==typeof window||"function"!=typeof MessageChannel){var n,r,i,a,o,s=null,u=null,c=function(){if(null!==s)try{var e=t.unstable_now();s(!0,e),s=null}catch(n){throw setTimeout(c,0),n}},l=Date.now();t.unstable_now=function(){return Date.now()-l},n=function(e){null!==s?setTimeout(n,0,e):(s=e,setTimeout(c,0))},r=function(e,t){u=setTimeout(e,t)},i=function(){clearTimeout(u)},a=function(){return!1},o=t.unstable_forceFrameRate=function(){}}else{var f=window.performance,d=window.Date,h=window.setTimeout,p=window.clearTimeout;if("undefined"!=typeof console){var b=window.cancelAnimationFrame;"function"!=typeof window.requestAnimationFrame&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"),"function"!=typeof b&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills")}if("object"==typeof f&&"function"==typeof f.now)t.unstable_now=function(){return f.now()};else{var m=d.now();t.unstable_now=function(){return d.now()-m}}var g=!1,v=null,y=-1,w=5,_=0;a=function(){return t.unstable_now()>=_},o=function(){},t.unstable_forceFrameRate=function(e){0>e||125M(o,n))void 0!==u&&0>M(u,o)?(e[r]=u,e[s]=n,r=s):(e[r]=o,e[a]=n,r=a);else if(void 0!==u&&0>M(u,n))e[r]=u,e[s]=n,r=s;else break a}}return t}return null}function M(e,t){var n=e.sortIndex-t.sortIndex;return 0!==n?n:e.id-t.id}var O=[],A=[],L=1,C=null,I=3,D=!1,N=!1,P=!1;function R(e){for(var t=x(A);null!==t;){if(null===t.callback)T(A);else if(t.startTime<=e)T(A),t.sortIndex=t.expirationTime,k(O,t);else break;t=x(A)}}function j(e){if(P=!1,R(e),!N){if(null!==x(O))N=!0,n(F);else{var t=x(A);null!==t&&r(j,t.startTime-e)}}}function F(e,n){N=!1,P&&(P=!1,i()),D=!0;var o=I;try{for(R(n),C=x(O);null!==C&&(!(C.expirationTime>n)||e&&!a());){var s=C.callback;if(null!==s){C.callback=null,I=C.priorityLevel;var u=s(C.expirationTime<=n);n=t.unstable_now(),"function"==typeof u?C.callback=u:C===x(O)&&T(O),R(n)}else T(O);C=x(O)}if(null!==C)var c=!0;else{var l=x(A);null!==l&&r(j,l.startTime-n),c=!1}return c}finally{C=null,I=o,D=!1}}function Y(e){switch(e){case 1:return -1;case 2:return 250;case 5:return 1073741823;case 4:return 1e4;default:return 5e3}}var B=o;t.unstable_ImmediatePriority=1,t.unstable_UserBlockingPriority=2,t.unstable_NormalPriority=3,t.unstable_IdlePriority=5,t.unstable_LowPriority=4,t.unstable_runWithPriority=function(e,t){switch(e){case 1:case 2:case 3:case 4:case 5:break;default:e=3}var n=I;I=e;try{return t()}finally{I=n}},t.unstable_next=function(e){switch(I){case 1:case 2:case 3:var t=3;break;default:t=I}var n=I;I=t;try{return e()}finally{I=n}},t.unstable_scheduleCallback=function(e,a,o){var s=t.unstable_now();if("object"==typeof o&&null!==o){var u=o.delay;u="number"==typeof u&&0s?(e.sortIndex=u,k(A,e),null===x(O)&&e===x(A)&&(P?i():P=!0,r(j,u-s))):(e.sortIndex=o,k(O,e),N||D||(N=!0,n(F))),e},t.unstable_cancelCallback=function(e){e.callback=null},t.unstable_wrapCallback=function(e){var t=I;return function(){var n=I;I=t;try{return e.apply(this,arguments)}finally{I=n}}},t.unstable_getCurrentPriorityLevel=function(){return I},t.unstable_shouldYield=function(){var e=t.unstable_now();R(e);var n=x(O);return n!==C&&null!==C&&null!==n&&null!==n.callback&&n.startTime<=e&&n.expirationTime>5==6?2:e>>4==14?3:e>>3==30?4:e>>6==2?-1:-2}function c(e,t,n){var r=t.length-1;if(r=0?(i>0&&(e.lastNeed=i-1),i):--r=0?(i>0&&(e.lastNeed=i-2),i):--r=0?(i>0&&(2===i?i=0:e.lastNeed=i-3),i):0}function l(e,t,n){if((192&t[0])!=128)return e.lastNeed=0,"�";if(e.lastNeed>1&&t.length>1){if((192&t[1])!=128)return e.lastNeed=1,"�";if(e.lastNeed>2&&t.length>2&&(192&t[2])!=128)return e.lastNeed=2,"�"}}function f(e){var t=this.lastTotal-this.lastNeed,n=l(this,e,t);return void 0!==n?n:this.lastNeed<=e.length?(e.copy(this.lastChar,t,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):void(e.copy(this.lastChar,t,0,e.length),this.lastNeed-=e.length)}function d(e,t){var n=c(this,e,t);if(!this.lastNeed)return e.toString("utf8",t);this.lastTotal=n;var r=e.length-(n-this.lastNeed);return e.copy(this.lastChar,0,r),e.toString("utf8",t,r)}function h(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+"�":t}function p(e,t){if((e.length-t)%2==0){var n=e.toString("utf16le",t);if(n){var r=n.charCodeAt(n.length-1);if(r>=55296&&r<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1],n.slice(0,-1)}return n}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=e[e.length-1],e.toString("utf16le",t,e.length-1)}function b(e){var t=e&&e.length?this.write(e):"";if(this.lastNeed){var n=this.lastTotal-this.lastNeed;return t+this.lastChar.toString("utf16le",0,n)}return t}function m(e,t){var n=(e.length-t)%3;return 0===n?e.toString("base64",t):(this.lastNeed=3-n,this.lastTotal=3,1===n?this.lastChar[0]=e[e.length-1]:(this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1]),e.toString("base64",t,e.length-n))}function g(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+this.lastChar.toString("base64",0,3-this.lastNeed):t}function v(e){return e.toString(this.encoding)}function y(e){return e&&e.length?this.write(e):""}t.s=s,s.prototype.write=function(e){var t,n;if(0===e.length)return"";if(this.lastNeed){if(void 0===(t=this.fillLast(e)))return"";n=this.lastNeed,this.lastNeed=0}else n=0;return n */ var r=n(48764),i=r.Buffer;function a(e,t){for(var n in e)t[n]=e[n]}function o(e,t,n){return i(e,t,n)}i.from&&i.alloc&&i.allocUnsafe&&i.allocUnsafeSlow?e.exports=r:(a(r,t),t.Buffer=o),o.prototype=Object.create(i.prototype),a(i,o),o.from=function(e,t,n){if("number"==typeof e)throw TypeError("Argument must not be a number");return i(e,t,n)},o.alloc=function(e,t,n){if("number"!=typeof e)throw TypeError("Argument must be a number");var r=i(e);return void 0!==t?"string"==typeof n?r.fill(t,n):r.fill(t):r.fill(0),r},o.allocUnsafe=function(e){if("number"!=typeof e)throw TypeError("Argument must be a number");return i(e)},o.allocUnsafeSlow=function(e){if("number"!=typeof e)throw TypeError("Argument must be a number");return r.SlowBuffer(e)}},93379(e,t,n){"use strict";var r,i,a=function(){return void 0===r&&(r=Boolean(window&&document&&document.all&&!window.atob)),r},o=(i={},function(e){if(void 0===i[e]){var t=document.querySelector(e);if(window.HTMLIFrameElement&&t instanceof window.HTMLIFrameElement)try{t=t.contentDocument.head}catch(n){t=null}i[e]=t}return i[e]}),s=[];function u(e){for(var t=-1,n=0;nOq});var r,i,a,o,s,u,c,l=n(67294),f=n.t(l,2),d=n(39814),h=n(5977),p=n(57209),b=n(32316),m=n(95880),g=n(17051),v=n(71381),y=n(81701),w=n(3022),_=n(60323),E=n(87591),S=n(25649),k=n(28902),x=n(71426),T=n(48884),M=n(94184),O=n.n(M),A=n(37703),L=n(73935),C=function(){if("undefined"!=typeof Map)return Map;function e(e,t){var n=-1;return e.some(function(e,r){return e[0]===t&&(n=r,!0)}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(t){var n=e(this.__entries__,t),r=this.__entries__[n];return r&&r[1]},t.prototype.set=function(t,n){var r=e(this.__entries__,t);~r?this.__entries__[r][1]=n:this.__entries__.push([t,n])},t.prototype.delete=function(t){var n=this.__entries__,r=e(n,t);~r&&n.splice(r,1)},t.prototype.has=function(t){return!!~e(this.__entries__,t)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(e,t){void 0===t&&(t=null);for(var n=0,r=this.__entries__;n0},e.prototype.connect_=function(){I&&!this.connected_&&(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),Y?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){I&&this.connected_&&(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(e){var t=e.propertyName,n=void 0===t?"":t;F.some(function(e){return!!~n.indexOf(e)})&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),U=function(e,t){for(var n=0,r=Object.keys(t);n0},e}(),er="undefined"!=typeof WeakMap?new WeakMap:new C,ei=function(){function e(t){if(!(this instanceof e))throw TypeError("Cannot call a class as a function.");if(!arguments.length)throw TypeError("1 argument required, but only 0 present.");var n=B.getInstance(),r=new en(t,n,this);er.set(this,r)}return e}();["observe","unobserve","disconnect"].forEach(function(e){ei.prototype[e]=function(){var t;return(t=er.get(this))[e].apply(t,arguments)}});var ea=void 0!==D.ResizeObserver?D.ResizeObserver:ei;let eo=ea;var es=function(e){var t=[],n=null,r=function(){for(var r=arguments.length,i=Array(r),a=0;a=t||n<0||f&&r>=a}function g(){var e=eb();if(m(e))return v(e);s=setTimeout(g,b(e))}function v(e){return(s=void 0,d&&r)?h(e):(r=i=void 0,o)}function y(){void 0!==s&&clearTimeout(s),c=0,r=u=i=s=void 0}function w(){return void 0===s?o:v(eb())}function _(){var e=eb(),n=m(e);if(r=arguments,i=this,u=e,n){if(void 0===s)return p(u);if(f)return clearTimeout(s),s=setTimeout(g,t),h(u)}return void 0===s&&(s=setTimeout(g,t)),o}return t=ez(t)||0,ed(n)&&(l=!!n.leading,a=(f="maxWait"in n)?eW(ez(n.maxWait)||0,t):a,d="trailing"in n?!!n.trailing:d),_.cancel=y,_.flush=w,_}let eq=eV;var eZ="Expected a function";function eX(e,t,n){var r=!0,i=!0;if("function"!=typeof e)throw TypeError(eZ);return ed(n)&&(r="leading"in n?!!n.leading:r,i="trailing"in n?!!n.trailing:i),eq(e,t,{leading:r,maxWait:t,trailing:i})}let eJ=eX;var eQ={debounce:eq,throttle:eJ},e1=function(e){return eQ[e]},e0=function(e){return"function"==typeof e},e2=function(){return"undefined"==typeof window},e3=function(e){return e instanceof Element||e instanceof HTMLDocument};function e4(e){return(e4="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function e6(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}function e5(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&l.createElement(tG.Z,{variant:"indeterminate",classes:r}))};tK.propTypes={fetchCount:el().number.isRequired};let tV=(0,b.withStyles)(tW)(tK);var tq=n(5536);let tZ=n.p+"ba8bbf16ebf8e1d05bef.svg";function tX(){return(tX=Object.assign||function(e){for(var t=1;t120){for(var d=Math.floor(u/80),h=u%80,p=[],b=0;b0},name:{enumerable:!1},nodes:{enumerable:!1},source:{enumerable:!1},positions:{enumerable:!1},originalError:{enumerable:!1}}),null!=s&&s.stack)?(Object.defineProperty(nf(b),"stack",{value:s.stack,writable:!0,configurable:!0}),nl(b)):(Error.captureStackTrace?Error.captureStackTrace(nf(b),n):Object.defineProperty(nf(b),"stack",{value:Error().stack,writable:!0,configurable:!0}),b)}return ns(n,[{key:"toString",value:function(){return nw(this)}},{key:t4.YF,get:function(){return"Object"}}]),n}(nd(Error));function ny(e){return void 0===e||0===e.length?void 0:e}function nw(e){var t=e.message;if(e.nodes)for(var n=0,r=e.nodes;n",EOF:"",BANG:"!",DOLLAR:"$",AMP:"&",PAREN_L:"(",PAREN_R:")",SPREAD:"...",COLON:":",EQUALS:"=",AT:"@",BRACKET_L:"[",BRACKET_R:"]",BRACE_L:"{",PIPE:"|",BRACE_R:"}",NAME:"Name",INT:"Int",FLOAT:"Float",STRING:"String",BLOCK_STRING:"BlockString",COMMENT:"Comment"}),nx=n(10143),nT=Object.freeze({QUERY:"QUERY",MUTATION:"MUTATION",SUBSCRIPTION:"SUBSCRIPTION",FIELD:"FIELD",FRAGMENT_DEFINITION:"FRAGMENT_DEFINITION",FRAGMENT_SPREAD:"FRAGMENT_SPREAD",INLINE_FRAGMENT:"INLINE_FRAGMENT",VARIABLE_DEFINITION:"VARIABLE_DEFINITION",SCHEMA:"SCHEMA",SCALAR:"SCALAR",OBJECT:"OBJECT",FIELD_DEFINITION:"FIELD_DEFINITION",ARGUMENT_DEFINITION:"ARGUMENT_DEFINITION",INTERFACE:"INTERFACE",UNION:"UNION",ENUM:"ENUM",ENUM_VALUE:"ENUM_VALUE",INPUT_OBJECT:"INPUT_OBJECT",INPUT_FIELD_DEFINITION:"INPUT_FIELD_DEFINITION"}),nM=n(87392),nO=function(){function e(e){var t=new nS.WU(nk.SOF,0,0,0,0,null);this.source=e,this.lastToken=t,this.token=t,this.line=1,this.lineStart=0}var t=e.prototype;return t.advance=function(){return this.lastToken=this.token,this.token=this.lookahead()},t.lookahead=function(){var e,t=this.token;if(t.kind!==nk.EOF)do t=null!==(e=t.next)&&void 0!==e?e:t.next=nC(this,t);while(t.kind===nk.COMMENT)return t},e}();function nA(e){return e===nk.BANG||e===nk.DOLLAR||e===nk.AMP||e===nk.PAREN_L||e===nk.PAREN_R||e===nk.SPREAD||e===nk.COLON||e===nk.EQUALS||e===nk.AT||e===nk.BRACKET_L||e===nk.BRACKET_R||e===nk.BRACE_L||e===nk.PIPE||e===nk.BRACE_R}function nL(e){return isNaN(e)?nk.EOF:e<127?JSON.stringify(String.fromCharCode(e)):'"\\u'.concat(("00"+e.toString(16).toUpperCase()).slice(-4),'"')}function nC(e,t){for(var n=e.source,r=n.body,i=r.length,a=t.end;a31||9===a))return new nS.WU(nk.COMMENT,t,s,n,r,i,o.slice(t+1,s))}function nN(e,t,n,r,i,a){var o=e.body,s=n,u=t,c=!1;if(45===s&&(s=o.charCodeAt(++u)),48===s){if((s=o.charCodeAt(++u))>=48&&s<=57)throw n_(e,u,"Invalid number, unexpected digit after 0: ".concat(nL(s),"."))}else u=nP(e,u,s),s=o.charCodeAt(u);if(46===s&&(c=!0,s=o.charCodeAt(++u),u=nP(e,u,s),s=o.charCodeAt(u)),(69===s||101===s)&&(c=!0,(43===(s=o.charCodeAt(++u))||45===s)&&(s=o.charCodeAt(++u)),u=nP(e,u,s),s=o.charCodeAt(u)),46===s||nU(s))throw n_(e,u,"Invalid number, expected digit but got: ".concat(nL(s),"."));return new nS.WU(c?nk.FLOAT:nk.INT,t,u,r,i,a,o.slice(t,u))}function nP(e,t,n){var r=e.body,i=t,a=n;if(a>=48&&a<=57){do a=r.charCodeAt(++i);while(a>=48&&a<=57)return i}throw n_(e,i,"Invalid number, expected digit but got: ".concat(nL(a),"."))}function nR(e,t,n,r,i){for(var a=e.body,o=t+1,s=o,u=0,c="";o=48&&e<=57?e-48:e>=65&&e<=70?e-55:e>=97&&e<=102?e-87:-1}function nB(e,t,n,r,i){for(var a=e.body,o=a.length,s=t+1,u=0;s!==o&&!isNaN(u=a.charCodeAt(s))&&(95===u||u>=48&&u<=57||u>=65&&u<=90||u>=97&&u<=122);)++s;return new nS.WU(nk.NAME,t,s,n,r,i,a.slice(t,s))}function nU(e){return 95===e||e>=65&&e<=90||e>=97&&e<=122}function nH(e,t){return new n$(e,t).parseDocument()}var n$=function(){function e(e,t){var n=(0,nx.T)(e)?e:new nx.H(e);this._lexer=new nO(n),this._options=t}var t=e.prototype;return t.parseName=function(){var e=this.expectToken(nk.NAME);return{kind:nE.h.NAME,value:e.value,loc:this.loc(e)}},t.parseDocument=function(){var e=this._lexer.token;return{kind:nE.h.DOCUMENT,definitions:this.many(nk.SOF,this.parseDefinition,nk.EOF),loc:this.loc(e)}},t.parseDefinition=function(){if(this.peek(nk.NAME))switch(this._lexer.token.value){case"query":case"mutation":case"subscription":return this.parseOperationDefinition();case"fragment":return this.parseFragmentDefinition();case"schema":case"scalar":case"type":case"interface":case"union":case"enum":case"input":case"directive":return this.parseTypeSystemDefinition();case"extend":return this.parseTypeSystemExtension()}else if(this.peek(nk.BRACE_L))return this.parseOperationDefinition();else if(this.peekDescription())return this.parseTypeSystemDefinition();throw this.unexpected()},t.parseOperationDefinition=function(){var e,t=this._lexer.token;if(this.peek(nk.BRACE_L))return{kind:nE.h.OPERATION_DEFINITION,operation:"query",name:void 0,variableDefinitions:[],directives:[],selectionSet:this.parseSelectionSet(),loc:this.loc(t)};var n=this.parseOperationType();return this.peek(nk.NAME)&&(e=this.parseName()),{kind:nE.h.OPERATION_DEFINITION,operation:n,name:e,variableDefinitions:this.parseVariableDefinitions(),directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(t)}},t.parseOperationType=function(){var e=this.expectToken(nk.NAME);switch(e.value){case"query":return"query";case"mutation":return"mutation";case"subscription":return"subscription"}throw this.unexpected(e)},t.parseVariableDefinitions=function(){return this.optionalMany(nk.PAREN_L,this.parseVariableDefinition,nk.PAREN_R)},t.parseVariableDefinition=function(){var e=this._lexer.token;return{kind:nE.h.VARIABLE_DEFINITION,variable:this.parseVariable(),type:(this.expectToken(nk.COLON),this.parseTypeReference()),defaultValue:this.expectOptionalToken(nk.EQUALS)?this.parseValueLiteral(!0):void 0,directives:this.parseDirectives(!0),loc:this.loc(e)}},t.parseVariable=function(){var e=this._lexer.token;return this.expectToken(nk.DOLLAR),{kind:nE.h.VARIABLE,name:this.parseName(),loc:this.loc(e)}},t.parseSelectionSet=function(){var e=this._lexer.token;return{kind:nE.h.SELECTION_SET,selections:this.many(nk.BRACE_L,this.parseSelection,nk.BRACE_R),loc:this.loc(e)}},t.parseSelection=function(){return this.peek(nk.SPREAD)?this.parseFragment():this.parseField()},t.parseField=function(){var e,t,n=this._lexer.token,r=this.parseName();return this.expectOptionalToken(nk.COLON)?(e=r,t=this.parseName()):t=r,{kind:nE.h.FIELD,alias:e,name:t,arguments:this.parseArguments(!1),directives:this.parseDirectives(!1),selectionSet:this.peek(nk.BRACE_L)?this.parseSelectionSet():void 0,loc:this.loc(n)}},t.parseArguments=function(e){var t=e?this.parseConstArgument:this.parseArgument;return this.optionalMany(nk.PAREN_L,t,nk.PAREN_R)},t.parseArgument=function(){var e=this._lexer.token,t=this.parseName();return this.expectToken(nk.COLON),{kind:nE.h.ARGUMENT,name:t,value:this.parseValueLiteral(!1),loc:this.loc(e)}},t.parseConstArgument=function(){var e=this._lexer.token;return{kind:nE.h.ARGUMENT,name:this.parseName(),value:(this.expectToken(nk.COLON),this.parseValueLiteral(!0)),loc:this.loc(e)}},t.parseFragment=function(){var e=this._lexer.token;this.expectToken(nk.SPREAD);var t=this.expectOptionalKeyword("on");return!t&&this.peek(nk.NAME)?{kind:nE.h.FRAGMENT_SPREAD,name:this.parseFragmentName(),directives:this.parseDirectives(!1),loc:this.loc(e)}:{kind:nE.h.INLINE_FRAGMENT,typeCondition:t?this.parseNamedType():void 0,directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(e)}},t.parseFragmentDefinition=function(){var e,t=this._lexer.token;return(this.expectKeyword("fragment"),(null===(e=this._options)||void 0===e?void 0:e.experimentalFragmentVariables)===!0)?{kind:nE.h.FRAGMENT_DEFINITION,name:this.parseFragmentName(),variableDefinitions:this.parseVariableDefinitions(),typeCondition:(this.expectKeyword("on"),this.parseNamedType()),directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(t)}:{kind:nE.h.FRAGMENT_DEFINITION,name:this.parseFragmentName(),typeCondition:(this.expectKeyword("on"),this.parseNamedType()),directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(t)}},t.parseFragmentName=function(){if("on"===this._lexer.token.value)throw this.unexpected();return this.parseName()},t.parseValueLiteral=function(e){var t=this._lexer.token;switch(t.kind){case nk.BRACKET_L:return this.parseList(e);case nk.BRACE_L:return this.parseObject(e);case nk.INT:return this._lexer.advance(),{kind:nE.h.INT,value:t.value,loc:this.loc(t)};case nk.FLOAT:return this._lexer.advance(),{kind:nE.h.FLOAT,value:t.value,loc:this.loc(t)};case nk.STRING:case nk.BLOCK_STRING:return this.parseStringLiteral();case nk.NAME:switch(this._lexer.advance(),t.value){case"true":return{kind:nE.h.BOOLEAN,value:!0,loc:this.loc(t)};case"false":return{kind:nE.h.BOOLEAN,value:!1,loc:this.loc(t)};case"null":return{kind:nE.h.NULL,loc:this.loc(t)};default:return{kind:nE.h.ENUM,value:t.value,loc:this.loc(t)}}case nk.DOLLAR:if(!e)return this.parseVariable()}throw this.unexpected()},t.parseStringLiteral=function(){var e=this._lexer.token;return this._lexer.advance(),{kind:nE.h.STRING,value:e.value,block:e.kind===nk.BLOCK_STRING,loc:this.loc(e)}},t.parseList=function(e){var t=this,n=this._lexer.token,r=function(){return t.parseValueLiteral(e)};return{kind:nE.h.LIST,values:this.any(nk.BRACKET_L,r,nk.BRACKET_R),loc:this.loc(n)}},t.parseObject=function(e){var t=this,n=this._lexer.token,r=function(){return t.parseObjectField(e)};return{kind:nE.h.OBJECT,fields:this.any(nk.BRACE_L,r,nk.BRACE_R),loc:this.loc(n)}},t.parseObjectField=function(e){var t=this._lexer.token,n=this.parseName();return this.expectToken(nk.COLON),{kind:nE.h.OBJECT_FIELD,name:n,value:this.parseValueLiteral(e),loc:this.loc(t)}},t.parseDirectives=function(e){for(var t=[];this.peek(nk.AT);)t.push(this.parseDirective(e));return t},t.parseDirective=function(e){var t=this._lexer.token;return this.expectToken(nk.AT),{kind:nE.h.DIRECTIVE,name:this.parseName(),arguments:this.parseArguments(e),loc:this.loc(t)}},t.parseTypeReference=function(){var e,t=this._lexer.token;return(this.expectOptionalToken(nk.BRACKET_L)?(e=this.parseTypeReference(),this.expectToken(nk.BRACKET_R),e={kind:nE.h.LIST_TYPE,type:e,loc:this.loc(t)}):e=this.parseNamedType(),this.expectOptionalToken(nk.BANG))?{kind:nE.h.NON_NULL_TYPE,type:e,loc:this.loc(t)}:e},t.parseNamedType=function(){var e=this._lexer.token;return{kind:nE.h.NAMED_TYPE,name:this.parseName(),loc:this.loc(e)}},t.parseTypeSystemDefinition=function(){var e=this.peekDescription()?this._lexer.lookahead():this._lexer.token;if(e.kind===nk.NAME)switch(e.value){case"schema":return this.parseSchemaDefinition();case"scalar":return this.parseScalarTypeDefinition();case"type":return this.parseObjectTypeDefinition();case"interface":return this.parseInterfaceTypeDefinition();case"union":return this.parseUnionTypeDefinition();case"enum":return this.parseEnumTypeDefinition();case"input":return this.parseInputObjectTypeDefinition();case"directive":return this.parseDirectiveDefinition()}throw this.unexpected(e)},t.peekDescription=function(){return this.peek(nk.STRING)||this.peek(nk.BLOCK_STRING)},t.parseDescription=function(){if(this.peekDescription())return this.parseStringLiteral()},t.parseSchemaDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("schema");var n=this.parseDirectives(!0),r=this.many(nk.BRACE_L,this.parseOperationTypeDefinition,nk.BRACE_R);return{kind:nE.h.SCHEMA_DEFINITION,description:t,directives:n,operationTypes:r,loc:this.loc(e)}},t.parseOperationTypeDefinition=function(){var e=this._lexer.token,t=this.parseOperationType();this.expectToken(nk.COLON);var n=this.parseNamedType();return{kind:nE.h.OPERATION_TYPE_DEFINITION,operation:t,type:n,loc:this.loc(e)}},t.parseScalarTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("scalar");var n=this.parseName(),r=this.parseDirectives(!0);return{kind:nE.h.SCALAR_TYPE_DEFINITION,description:t,name:n,directives:r,loc:this.loc(e)}},t.parseObjectTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("type");var n=this.parseName(),r=this.parseImplementsInterfaces(),i=this.parseDirectives(!0),a=this.parseFieldsDefinition();return{kind:nE.h.OBJECT_TYPE_DEFINITION,description:t,name:n,interfaces:r,directives:i,fields:a,loc:this.loc(e)}},t.parseImplementsInterfaces=function(){var e;if(!this.expectOptionalKeyword("implements"))return[];if((null===(e=this._options)||void 0===e?void 0:e.allowLegacySDLImplementsInterfaces)===!0){var t=[];this.expectOptionalToken(nk.AMP);do t.push(this.parseNamedType());while(this.expectOptionalToken(nk.AMP)||this.peek(nk.NAME))return t}return this.delimitedMany(nk.AMP,this.parseNamedType)},t.parseFieldsDefinition=function(){var e;return(null===(e=this._options)||void 0===e?void 0:e.allowLegacySDLEmptyFields)===!0&&this.peek(nk.BRACE_L)&&this._lexer.lookahead().kind===nk.BRACE_R?(this._lexer.advance(),this._lexer.advance(),[]):this.optionalMany(nk.BRACE_L,this.parseFieldDefinition,nk.BRACE_R)},t.parseFieldDefinition=function(){var e=this._lexer.token,t=this.parseDescription(),n=this.parseName(),r=this.parseArgumentDefs();this.expectToken(nk.COLON);var i=this.parseTypeReference(),a=this.parseDirectives(!0);return{kind:nE.h.FIELD_DEFINITION,description:t,name:n,arguments:r,type:i,directives:a,loc:this.loc(e)}},t.parseArgumentDefs=function(){return this.optionalMany(nk.PAREN_L,this.parseInputValueDef,nk.PAREN_R)},t.parseInputValueDef=function(){var e,t=this._lexer.token,n=this.parseDescription(),r=this.parseName();this.expectToken(nk.COLON);var i=this.parseTypeReference();this.expectOptionalToken(nk.EQUALS)&&(e=this.parseValueLiteral(!0));var a=this.parseDirectives(!0);return{kind:nE.h.INPUT_VALUE_DEFINITION,description:n,name:r,type:i,defaultValue:e,directives:a,loc:this.loc(t)}},t.parseInterfaceTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("interface");var n=this.parseName(),r=this.parseImplementsInterfaces(),i=this.parseDirectives(!0),a=this.parseFieldsDefinition();return{kind:nE.h.INTERFACE_TYPE_DEFINITION,description:t,name:n,interfaces:r,directives:i,fields:a,loc:this.loc(e)}},t.parseUnionTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("union");var n=this.parseName(),r=this.parseDirectives(!0),i=this.parseUnionMemberTypes();return{kind:nE.h.UNION_TYPE_DEFINITION,description:t,name:n,directives:r,types:i,loc:this.loc(e)}},t.parseUnionMemberTypes=function(){return this.expectOptionalToken(nk.EQUALS)?this.delimitedMany(nk.PIPE,this.parseNamedType):[]},t.parseEnumTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("enum");var n=this.parseName(),r=this.parseDirectives(!0),i=this.parseEnumValuesDefinition();return{kind:nE.h.ENUM_TYPE_DEFINITION,description:t,name:n,directives:r,values:i,loc:this.loc(e)}},t.parseEnumValuesDefinition=function(){return this.optionalMany(nk.BRACE_L,this.parseEnumValueDefinition,nk.BRACE_R)},t.parseEnumValueDefinition=function(){var e=this._lexer.token,t=this.parseDescription(),n=this.parseName(),r=this.parseDirectives(!0);return{kind:nE.h.ENUM_VALUE_DEFINITION,description:t,name:n,directives:r,loc:this.loc(e)}},t.parseInputObjectTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("input");var n=this.parseName(),r=this.parseDirectives(!0),i=this.parseInputFieldsDefinition();return{kind:nE.h.INPUT_OBJECT_TYPE_DEFINITION,description:t,name:n,directives:r,fields:i,loc:this.loc(e)}},t.parseInputFieldsDefinition=function(){return this.optionalMany(nk.BRACE_L,this.parseInputValueDef,nk.BRACE_R)},t.parseTypeSystemExtension=function(){var e=this._lexer.lookahead();if(e.kind===nk.NAME)switch(e.value){case"schema":return this.parseSchemaExtension();case"scalar":return this.parseScalarTypeExtension();case"type":return this.parseObjectTypeExtension();case"interface":return this.parseInterfaceTypeExtension();case"union":return this.parseUnionTypeExtension();case"enum":return this.parseEnumTypeExtension();case"input":return this.parseInputObjectTypeExtension()}throw this.unexpected(e)},t.parseSchemaExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("schema");var t=this.parseDirectives(!0),n=this.optionalMany(nk.BRACE_L,this.parseOperationTypeDefinition,nk.BRACE_R);if(0===t.length&&0===n.length)throw this.unexpected();return{kind:nE.h.SCHEMA_EXTENSION,directives:t,operationTypes:n,loc:this.loc(e)}},t.parseScalarTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("scalar");var t=this.parseName(),n=this.parseDirectives(!0);if(0===n.length)throw this.unexpected();return{kind:nE.h.SCALAR_TYPE_EXTENSION,name:t,directives:n,loc:this.loc(e)}},t.parseObjectTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("type");var t=this.parseName(),n=this.parseImplementsInterfaces(),r=this.parseDirectives(!0),i=this.parseFieldsDefinition();if(0===n.length&&0===r.length&&0===i.length)throw this.unexpected();return{kind:nE.h.OBJECT_TYPE_EXTENSION,name:t,interfaces:n,directives:r,fields:i,loc:this.loc(e)}},t.parseInterfaceTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("interface");var t=this.parseName(),n=this.parseImplementsInterfaces(),r=this.parseDirectives(!0),i=this.parseFieldsDefinition();if(0===n.length&&0===r.length&&0===i.length)throw this.unexpected();return{kind:nE.h.INTERFACE_TYPE_EXTENSION,name:t,interfaces:n,directives:r,fields:i,loc:this.loc(e)}},t.parseUnionTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("union");var t=this.parseName(),n=this.parseDirectives(!0),r=this.parseUnionMemberTypes();if(0===n.length&&0===r.length)throw this.unexpected();return{kind:nE.h.UNION_TYPE_EXTENSION,name:t,directives:n,types:r,loc:this.loc(e)}},t.parseEnumTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("enum");var t=this.parseName(),n=this.parseDirectives(!0),r=this.parseEnumValuesDefinition();if(0===n.length&&0===r.length)throw this.unexpected();return{kind:nE.h.ENUM_TYPE_EXTENSION,name:t,directives:n,values:r,loc:this.loc(e)}},t.parseInputObjectTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("input");var t=this.parseName(),n=this.parseDirectives(!0),r=this.parseInputFieldsDefinition();if(0===n.length&&0===r.length)throw this.unexpected();return{kind:nE.h.INPUT_OBJECT_TYPE_EXTENSION,name:t,directives:n,fields:r,loc:this.loc(e)}},t.parseDirectiveDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("directive"),this.expectToken(nk.AT);var n=this.parseName(),r=this.parseArgumentDefs(),i=this.expectOptionalKeyword("repeatable");this.expectKeyword("on");var a=this.parseDirectiveLocations();return{kind:nE.h.DIRECTIVE_DEFINITION,description:t,name:n,arguments:r,repeatable:i,locations:a,loc:this.loc(e)}},t.parseDirectiveLocations=function(){return this.delimitedMany(nk.PIPE,this.parseDirectiveLocation)},t.parseDirectiveLocation=function(){var e=this._lexer.token,t=this.parseName();if(void 0!==nT[t.value])return t;throw this.unexpected(e)},t.loc=function(e){var t;if((null===(t=this._options)||void 0===t?void 0:t.noLocation)!==!0)return new nS.Ye(e,this._lexer.lastToken,this._lexer.source)},t.peek=function(e){return this._lexer.token.kind===e},t.expectToken=function(e){var t=this._lexer.token;if(t.kind===e)return this._lexer.advance(),t;throw n_(this._lexer.source,t.start,"Expected ".concat(nG(e),", found ").concat(nz(t),"."))},t.expectOptionalToken=function(e){var t=this._lexer.token;if(t.kind===e)return this._lexer.advance(),t},t.expectKeyword=function(e){var t=this._lexer.token;if(t.kind===nk.NAME&&t.value===e)this._lexer.advance();else throw n_(this._lexer.source,t.start,'Expected "'.concat(e,'", found ').concat(nz(t),"."))},t.expectOptionalKeyword=function(e){var t=this._lexer.token;return t.kind===nk.NAME&&t.value===e&&(this._lexer.advance(),!0)},t.unexpected=function(e){var t=null!=e?e:this._lexer.token;return n_(this._lexer.source,t.start,"Unexpected ".concat(nz(t),"."))},t.any=function(e,t,n){this.expectToken(e);for(var r=[];!this.expectOptionalToken(n);)r.push(t.call(this));return r},t.optionalMany=function(e,t,n){if(this.expectOptionalToken(e)){var r=[];do r.push(t.call(this));while(!this.expectOptionalToken(n))return r}return[]},t.many=function(e,t,n){this.expectToken(e);var r=[];do r.push(t.call(this));while(!this.expectOptionalToken(n))return r},t.delimitedMany=function(e,t){this.expectOptionalToken(e);var n=[];do n.push(t.call(this));while(this.expectOptionalToken(e))return n},e}();function nz(e){var t=e.value;return nG(e.kind)+(null!=t?' "'.concat(t,'"'):"")}function nG(e){return nA(e)?'"'.concat(e,'"'):e}var nW=new Map,nK=new Map,nV=!0,nq=!1;function nZ(e){return e.replace(/[\s,]+/g," ").trim()}function nX(e){return nZ(e.source.body.substring(e.start,e.end))}function nJ(e){var t=new Set,n=[];return e.definitions.forEach(function(e){if("FragmentDefinition"===e.kind){var r=e.name.value,i=nX(e.loc),a=nK.get(r);a&&!a.has(i)?nV&&console.warn("Warning: fragment with name "+r+" already exists.\ngraphql-tag enforces all fragment names across your application to be unique; read more about\nthis in the docs: http://dev.apollodata.com/core/fragments.html#unique-names"):a||nK.set(r,a=new Set),a.add(i),t.has(i)||(t.add(i),n.push(e))}else n.push(e)}),(0,t0.pi)((0,t0.pi)({},e),{definitions:n})}function nQ(e){var t=new Set(e.definitions);t.forEach(function(e){e.loc&&delete e.loc,Object.keys(e).forEach(function(n){var r=e[n];r&&"object"==typeof r&&t.add(r)})});var n=e.loc;return n&&(delete n.startToken,delete n.endToken),e}function n1(e){var t=nZ(e);if(!nW.has(t)){var n=nH(e,{experimentalFragmentVariables:nq,allowLegacyFragmentVariables:nq});if(!n||"Document"!==n.kind)throw Error("Not a valid GraphQL document.");nW.set(t,nQ(nJ(n)))}return nW.get(t)}function n0(e){for(var t=[],n=1;n, or pass an ApolloClient instance in via options.'):(0,n9.kG)(!!n,32),n}var rp=n(10542),rb=n(53712),rm=n(21436),rg=Object.prototype.hasOwnProperty;function rv(e,t){return void 0===t&&(t=Object.create(null)),ry(rh(t.client),e).useQuery(t)}function ry(e,t){var n=(0,l.useRef)();n.current&&e===n.current.client&&t===n.current.query||(n.current=new rw(e,t,n.current));var r=n.current,i=(0,l.useState)(0),a=(i[0],i[1]);return r.forceUpdate=function(){a(function(e){return e+1})},r}var rw=function(){function e(e,t,n){this.client=e,this.query=t,this.ssrDisabledResult=(0,rp.J)({loading:!0,data:void 0,error:void 0,networkStatus:ru.I.loading}),this.skipStandbyResult=(0,rp.J)({loading:!1,data:void 0,error:void 0,networkStatus:ru.I.ready}),this.toQueryResultCache=new(n7.mr?WeakMap:Map),rd(t,r.Query);var i=n&&n.result,a=i&&i.data;a&&(this.previousData=a)}return e.prototype.forceUpdate=function(){__DEV__&&n9.kG.warn("Calling default no-op implementation of InternalState#forceUpdate")},e.prototype.executeQuery=function(e){var t,n=this;e.query&&Object.assign(this,{query:e.query}),this.watchQueryOptions=this.createWatchQueryOptions(this.queryHookOptions=e);var r=this.observable.reobserveAsConcast(this.getObsQueryOptions());return this.previousData=(null===(t=this.result)||void 0===t?void 0:t.data)||this.previousData,this.result=void 0,this.forceUpdate(),new Promise(function(e){var t;r.subscribe({next:function(e){t=e},error:function(){e(n.toQueryResult(n.observable.getCurrentResult()))},complete:function(){e(n.toQueryResult(t))}})})},e.prototype.useQuery=function(e){var t=this;this.renderPromises=(0,l.useContext)((0,ro.K)()).renderPromises,this.useOptions(e);var n=this.useObservableQuery(),r=rt((0,l.useCallback)(function(){if(t.renderPromises)return function(){};var e=function(){var e=t.result,r=n.getCurrentResult();!(e&&e.loading===r.loading&&e.networkStatus===r.networkStatus&&(0,ri.D)(e.data,r.data))&&t.setResult(r)},r=function(a){var o=n.last;i.unsubscribe();try{n.resetLastResults(),i=n.subscribe(e,r)}finally{n.last=o}if(!rg.call(a,"graphQLErrors"))throw a;var s=t.result;(!s||s&&s.loading||!(0,ri.D)(a,s.error))&&t.setResult({data:s&&s.data,error:a,loading:!1,networkStatus:ru.I.error})},i=n.subscribe(e,r);return function(){return setTimeout(function(){return i.unsubscribe()})}},[n,this.renderPromises,this.client.disableNetworkFetches,]),function(){return t.getCurrentResult()},function(){return t.getCurrentResult()});return this.unsafeHandlePartialRefetch(r),this.toQueryResult(r)},e.prototype.useOptions=function(t){var n,r=this.createWatchQueryOptions(this.queryHookOptions=t),i=this.watchQueryOptions;!(0,ri.D)(r,i)&&(this.watchQueryOptions=r,i&&this.observable&&(this.observable.reobserve(this.getObsQueryOptions()),this.previousData=(null===(n=this.result)||void 0===n?void 0:n.data)||this.previousData,this.result=void 0)),this.onCompleted=t.onCompleted||e.prototype.onCompleted,this.onError=t.onError||e.prototype.onError,(this.renderPromises||this.client.disableNetworkFetches)&&!1===this.queryHookOptions.ssr&&!this.queryHookOptions.skip?this.result=this.ssrDisabledResult:this.queryHookOptions.skip||"standby"===this.watchQueryOptions.fetchPolicy?this.result=this.skipStandbyResult:(this.result===this.ssrDisabledResult||this.result===this.skipStandbyResult)&&(this.result=void 0)},e.prototype.getObsQueryOptions=function(){var e=[],t=this.client.defaultOptions.watchQuery;return t&&e.push(t),this.queryHookOptions.defaultOptions&&e.push(this.queryHookOptions.defaultOptions),e.push((0,rb.o)(this.observable&&this.observable.options,this.watchQueryOptions)),e.reduce(ra.J)},e.prototype.createWatchQueryOptions=function(e){void 0===e&&(e={});var t,n=e.skip,r=Object.assign((e.ssr,e.onCompleted,e.onError,e.defaultOptions,(0,t0._T)(e,["skip","ssr","onCompleted","onError","defaultOptions"])),{query:this.query});if(this.renderPromises&&("network-only"===r.fetchPolicy||"cache-and-network"===r.fetchPolicy)&&(r.fetchPolicy="cache-first"),r.variables||(r.variables={}),n){var i=r.fetchPolicy,a=void 0===i?this.getDefaultFetchPolicy():i,o=r.initialFetchPolicy;Object.assign(r,{initialFetchPolicy:void 0===o?a:o,fetchPolicy:"standby"})}else r.fetchPolicy||(r.fetchPolicy=(null===(t=this.observable)||void 0===t?void 0:t.options.initialFetchPolicy)||this.getDefaultFetchPolicy());return r},e.prototype.getDefaultFetchPolicy=function(){var e,t;return(null===(e=this.queryHookOptions.defaultOptions)||void 0===e?void 0:e.fetchPolicy)||(null===(t=this.client.defaultOptions.watchQuery)||void 0===t?void 0:t.fetchPolicy)||"cache-first"},e.prototype.onCompleted=function(e){},e.prototype.onError=function(e){},e.prototype.useObservableQuery=function(){var e=this.observable=this.renderPromises&&this.renderPromises.getSSRObservable(this.watchQueryOptions)||this.observable||this.client.watchQuery(this.getObsQueryOptions());this.obsQueryFields=(0,l.useMemo)(function(){return{refetch:e.refetch.bind(e),reobserve:e.reobserve.bind(e),fetchMore:e.fetchMore.bind(e),updateQuery:e.updateQuery.bind(e),startPolling:e.startPolling.bind(e),stopPolling:e.stopPolling.bind(e),subscribeToMore:e.subscribeToMore.bind(e)}},[e]);var t=!(!1===this.queryHookOptions.ssr||this.queryHookOptions.skip);return this.renderPromises&&t&&(this.renderPromises.registerSSRObservable(e),e.getCurrentResult().loading&&this.renderPromises.addObservableQueryPromise(e)),e},e.prototype.setResult=function(e){var t=this.result;t&&t.data&&(this.previousData=t.data),this.result=e,this.forceUpdate(),this.handleErrorOrCompleted(e)},e.prototype.handleErrorOrCompleted=function(e){var t=this;if(!e.loading){var n=this.toApolloError(e);Promise.resolve().then(function(){n?t.onError(n):e.data&&t.onCompleted(e.data)}).catch(function(e){__DEV__&&n9.kG.warn(e)})}},e.prototype.toApolloError=function(e){return(0,rm.O)(e.errors)?new rs.cA({graphQLErrors:e.errors}):e.error},e.prototype.getCurrentResult=function(){return this.result||this.handleErrorOrCompleted(this.result=this.observable.getCurrentResult()),this.result},e.prototype.toQueryResult=function(e){var t=this.toQueryResultCache.get(e);if(t)return t;var n=e.data,r=(e.partial,(0,t0._T)(e,["data","partial"]));return this.toQueryResultCache.set(e,t=(0,t0.pi)((0,t0.pi)((0,t0.pi)({data:n},r),this.obsQueryFields),{client:this.client,observable:this.observable,variables:this.observable.variables,called:!this.queryHookOptions.skip,previousData:this.previousData})),!t.error&&(0,rm.O)(e.errors)&&(t.error=new rs.cA({graphQLErrors:e.errors})),t},e.prototype.unsafeHandlePartialRefetch=function(e){e.partial&&this.queryHookOptions.partialRefetch&&!e.loading&&(!e.data||0===Object.keys(e.data).length)&&"cache-only"!==this.observable.options.fetchPolicy&&(Object.assign(e,{loading:!0,networkStatus:ru.I.refetch}),this.observable.refetch())},e}();function r_(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&void 0!==arguments[0]?arguments[0]:{};return rv(iH,e)},iz=function(){var e=ij(),t=parseInt(e.get("page")||"1",10),n=parseInt(e.get("per")||"50",10),r=i$({variables:{offset:(t-1)*n,limit:n},fetchPolicy:"network-only"}),i=r.data,a=r.loading,o=r.error;return a?l.createElement(iR,null):o?l.createElement(iD,{error:o}):i?l.createElement(iI,{chains:i.chains.results,page:t,pageSize:n,total:i.chains.metadata.total}):null},iG=n(67932),iW=n(8126),iK="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function iV(e){if(iq())return Intl.DateTimeFormat.supportedLocalesOf(e)[0]}function iq(){return("undefined"==typeof Intl?"undefined":iK(Intl))==="object"&&"function"==typeof Intl.DateTimeFormat}var iZ="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},iX=function(){function e(e,t){for(var n=0;n=i.length)break;s=i[o++]}else{if((o=i.next()).done)break;s=o.value}var s,u=s;if((void 0===e?"undefined":iZ(e))!=="object")return;e=e[u]}return e}},{key:"put",value:function(){for(var e=arguments.length,t=Array(e),n=0;n=o.length)break;c=o[u++]}else{if((u=o.next()).done)break;c=u.value}var c,l=c;"object"!==iZ(a[l])&&(a[l]={}),a=a[l]}return a[i]=r}}]),e}();let i1=iQ;var i0=new i1;function i2(e,t){if(!iq())return function(e){return e.toString()};var n=i4(e),r=JSON.stringify(t),i=i0.get(String(n),r)||i0.put(String(n),r,new Intl.DateTimeFormat(n,t));return function(e){return i.format(e)}}var i3={};function i4(e){var t=e.toString();return i3[t]?i3[t]:i3[t]=iV(e)}var i6="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function i5(e){return i8(e)?e:new Date(e)}function i8(e){return e instanceof Date||i9(e)}function i9(e){return(void 0===e?"undefined":i6(e))==="object"&&"function"==typeof e.getTime}var i7=n(54087),ae=n.n(i7);function at(e,t){if(0===e.length)return 0;for(var n=0,r=e.length-1,i=void 0;n<=r;){var a=t(e[i=Math.floor((r+n)/2)]);if(0===a)return i;if(a<0){if((n=i+1)>r)return n}else if((r=i-1)=t.nextUpdateTime)aa(t,this.instances);else break}},scheduleNextTick:function(){var e=this;this.scheduledTick=ae()(function(){e.tick(),e.scheduleNextTick()})},start:function(){this.scheduleNextTick()},stop:function(){ae().cancel(this.scheduledTick)}};function ai(e){var t=an(e.getNextValue(),2),n=t[0],r=t[1];e.setValue(n),e.nextUpdateTime=r}function aa(e,t){ai(e),as(t,e),ao(t,e)}function ao(e,t){var n=au(e,t);e.splice(n,0,t)}function as(e,t){var n=e.indexOf(t);e.splice(n,1)}function au(e,t){var n=t.nextUpdateTime;return at(e,function(e){return e.nextUpdateTime===n?0:e.nextUpdateTime>n?1:-1})}var ac=(0,ec.oneOfType)([(0,ec.shape)({minTime:ec.number,formatAs:ec.string.isRequired}),(0,ec.shape)({test:ec.func,formatAs:ec.string.isRequired}),(0,ec.shape)({minTime:ec.number,format:ec.func.isRequired}),(0,ec.shape)({test:ec.func,format:ec.func.isRequired})]),al=(0,ec.oneOfType)([ec.string,(0,ec.shape)({steps:(0,ec.arrayOf)(ac).isRequired,labels:(0,ec.oneOfType)([ec.string,(0,ec.arrayOf)(ec.string)]).isRequired,round:ec.string})]),af=Object.assign||function(e){for(var t=1;t=0)&&Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function ap(e){var t=e.date,n=e.future,r=e.timeStyle,i=e.round,a=e.minTimeLeft,o=e.tooltip,s=e.component,u=e.container,c=e.wrapperComponent,f=e.wrapperProps,d=e.locale,h=e.locales,p=e.formatVerboseDate,b=e.verboseDateFormat,m=e.updateInterval,g=e.tick,v=ah(e,["date","future","timeStyle","round","minTimeLeft","tooltip","component","container","wrapperComponent","wrapperProps","locale","locales","formatVerboseDate","verboseDateFormat","updateInterval","tick"]),y=(0,l.useMemo)(function(){return d&&(h=[d]),h.concat(iW.Z.getDefaultLocale())},[d,h]),w=(0,l.useMemo)(function(){return new iW.Z(y)},[y]);t=(0,l.useMemo)(function(){return i5(t)},[t]);var _=(0,l.useCallback)(function(){var e=Date.now(),o=void 0;if(n&&e>=t.getTime()&&(e=t.getTime(),o=!0),void 0!==a){var s=t.getTime()-1e3*a;e>s&&(e=s,o=!0)}var u=w.format(t,r,{getTimeToNextUpdate:!0,now:e,future:n,round:i}),c=ad(u,2),l=c[0],f=c[1];return f=o?ag:m||f||6e4,[l,e+f]},[t,n,r,m,i,a,w]),E=(0,l.useRef)();E.current=_;var S=(0,l.useMemo)(_,[]),k=ad(S,2),x=k[0],T=k[1],M=(0,l.useState)(x),O=ad(M,2),A=O[0],L=O[1],C=ad((0,l.useState)(),2),I=C[0],D=C[1],N=(0,l.useRef)();(0,l.useEffect)(function(){if(g)return N.current=ar.add({getNextValue:function(){return E.current()},setValue:L,nextUpdateTime:T}),function(){return N.current.stop()}},[g]),(0,l.useEffect)(function(){if(N.current)N.current.forceUpdate();else{var e=_(),t=ad(e,1)[0];L(t)}},[_]),(0,l.useEffect)(function(){D(!0)},[]);var P=(0,l.useMemo)(function(){if("undefined"!=typeof window)return i2(y,b)},[y,b]),R=(0,l.useMemo)(function(){if("undefined"!=typeof window)return p?p(t):P(t)},[t,p,P]),j=l.createElement(s,af({date:t,verboseDate:I?R:void 0,tooltip:o},v),A),F=c||u;return F?l.createElement(F,af({},f,{verboseDate:I?R:void 0}),j):j}ap.propTypes={date:el().oneOfType([el().instanceOf(Date),el().number]).isRequired,locale:el().string,locales:el().arrayOf(el().string),future:el().bool,timeStyle:al,round:el().string,minTimeLeft:el().number,component:el().elementType.isRequired,tooltip:el().bool.isRequired,formatVerboseDate:el().func,verboseDateFormat:el().object,updateInterval:el().oneOfType([el().number,el().arrayOf(el().shape({threshold:el().number,interval:el().number.isRequired}))]),tick:el().bool,wrapperComponent:el().func,wrapperProps:el().object},ap.defaultProps={locales:[],component:av,tooltip:!0,verboseDateFormat:{weekday:"long",day:"numeric",month:"long",year:"numeric",hour:"numeric",minute:"2-digit",second:"2-digit"},tick:!0},ap=l.memo(ap);let ab=ap;var am,ag=31536e9;function av(e){var t=e.date,n=e.verboseDate,r=e.tooltip,i=e.children,a=ah(e,["date","verboseDate","tooltip","children"]),o=(0,l.useMemo)(function(){return t.toISOString()},[t]);return l.createElement("time",af({},a,{dateTime:o,title:r?n:void 0}),i)}av.propTypes={date:el().instanceOf(Date).isRequired,verboseDate:el().string,tooltip:el().bool.isRequired,children:el().string.isRequired};var ay=n(30381),aw=n.n(ay),a_=n(31657);function aE(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function aS(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0?new rs.cA({graphQLErrors:i}):void 0;if(u===s.current.mutationId&&!c.ignoreResults){var f={called:!0,loading:!1,data:r,error:l,client:a};s.current.isMounted&&!(0,ri.D)(s.current.result,f)&&o(s.current.result=f)}var d=e.onCompleted||(null===(n=s.current.options)||void 0===n?void 0:n.onCompleted);return null==d||d(t.data,c),t}).catch(function(t){if(u===s.current.mutationId&&s.current.isMounted){var n,r={loading:!1,error:t,data:void 0,called:!0,client:a};(0,ri.D)(s.current.result,r)||o(s.current.result=r)}var i=e.onError||(null===(n=s.current.options)||void 0===n?void 0:n.onError);if(i)return i(t,c),{data:void 0,errors:t};throw t})},[]),c=(0,l.useCallback)(function(){s.current.isMounted&&o({called:!1,loading:!1,client:n})},[]);return(0,l.useEffect)(function(){return s.current.isMounted=!0,function(){s.current.isMounted=!1}},[]),[u,(0,t0.pi)({reset:c},a)]}var os=n(59067),ou=n(28428),oc=n(11186),ol=n(78513);function of(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var od=function(e){return(0,b.createStyles)({paper:{display:"flex",margin:"".concat(2.5*e.spacing.unit,"px 0"),padding:"".concat(3*e.spacing.unit,"px ").concat(3.5*e.spacing.unit,"px")},content:{flex:1,width:"100%"},actions:of({marginTop:-(1.5*e.spacing.unit),marginLeft:-(4*e.spacing.unit)},e.breakpoints.up("sm"),{marginLeft:0,marginRight:-(1.5*e.spacing.unit)}),itemBlock:{border:"1px solid rgba(224, 224, 224, 1)",borderRadius:e.shape.borderRadius,padding:2*e.spacing.unit,marginTop:e.spacing.unit},itemBlockText:{overflowWrap:"anywhere"}})},oh=(0,b.withStyles)(od)(function(e){var t=e.actions,n=e.children,r=e.classes;return l.createElement(ii.default,{className:r.paper},l.createElement("div",{className:r.content},n),t&&l.createElement("div",{className:r.actions},t))}),op=function(e){var t=e.title;return l.createElement(x.default,{variant:"subtitle2",gutterBottom:!0},t)},ob=function(e){var t=e.children,n=e.value;return l.createElement(x.default,{variant:"body1",noWrap:!0},t||n)},om=(0,b.withStyles)(od)(function(e){var t=e.children,n=e.classes,r=e.value;return l.createElement("div",{className:n.itemBlock},l.createElement(x.default,{variant:"body1",className:n.itemBlockText},t||r))});function og(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]-1}let sq=sV;function sZ(e,t){var n=this.__data__,r=sH(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this}let sX=sZ;function sJ(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t-1&&e%1==0&&e-1&&e%1==0&&e<=cC}let cD=cI;var cN="[object Arguments]",cP="[object Array]",cR="[object Boolean]",cj="[object Date]",cF="[object Error]",cY="[object Function]",cB="[object Map]",cU="[object Number]",cH="[object Object]",c$="[object RegExp]",cz="[object Set]",cG="[object String]",cW="[object WeakMap]",cK="[object ArrayBuffer]",cV="[object DataView]",cq="[object Float64Array]",cZ="[object Int8Array]",cX="[object Int16Array]",cJ="[object Int32Array]",cQ="[object Uint8Array]",c1="[object Uint8ClampedArray]",c0="[object Uint16Array]",c2="[object Uint32Array]",c3={};function c4(e){return eD(e)&&cD(e.length)&&!!c3[eC(e)]}c3["[object Float32Array]"]=c3[cq]=c3[cZ]=c3[cX]=c3[cJ]=c3[cQ]=c3[c1]=c3[c0]=c3[c2]=!0,c3[cN]=c3[cP]=c3[cK]=c3[cR]=c3[cV]=c3[cj]=c3[cF]=c3[cY]=c3[cB]=c3[cU]=c3[cH]=c3[c$]=c3[cz]=c3[cG]=c3[cW]=!1;let c6=c4;function c5(e){return function(t){return e(t)}}let c8=c5;var c9=n(79730),c7=c9.Z&&c9.Z.isTypedArray,le=c7?c8(c7):c6;let lt=le;var ln=Object.prototype.hasOwnProperty;function lr(e,t){var n=cx(e),r=!n&&cS(e),i=!n&&!r&&(0,cT.Z)(e),a=!n&&!r&&!i&<(e),o=n||r||i||a,s=o?cb(e.length,String):[],u=s.length;for(var c in e)(t||ln.call(e,c))&&!(o&&("length"==c||i&&("offset"==c||"parent"==c)||a&&("buffer"==c||"byteLength"==c||"byteOffset"==c)||cL(c,u)))&&s.push(c);return s}let li=lr;var la=Object.prototype;function lo(e){var t=e&&e.constructor;return e===("function"==typeof t&&t.prototype||la)}let ls=lo;var lu=sT(Object.keys,Object);let lc=lu;var ll=Object.prototype.hasOwnProperty;function lf(e){if(!ls(e))return lc(e);var t=[];for(var n in Object(e))ll.call(e,n)&&"constructor"!=n&&t.push(n);return t}let ld=lf;function lh(e){return null!=e&&cD(e.length)&&!ur(e)}let lp=lh;function lb(e){return lp(e)?li(e):ld(e)}let lm=lb;function lg(e,t){return e&&ch(t,lm(t),e)}let lv=lg;function ly(e){var t=[];if(null!=e)for(var n in Object(e))t.push(n);return t}let lw=ly;var l_=Object.prototype.hasOwnProperty;function lE(e){if(!ed(e))return lw(e);var t=ls(e),n=[];for(var r in e)"constructor"==r&&(t||!l_.call(e,r))||n.push(r);return n}let lS=lE;function lk(e){return lp(e)?li(e,!0):lS(e)}let lx=lk;function lT(e,t){return e&&ch(t,lx(t),e)}let lM=lT;var lO=n(42896);function lA(e,t){var n=-1,r=e.length;for(t||(t=Array(r));++n=0||(i[n]=e[n]);return i}function hu(e){if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}var hc=function(e){return Array.isArray(e)&&0===e.length},hl=function(e){return"function"==typeof e},hf=function(e){return null!==e&&"object"==typeof e},hd=function(e){return String(Math.floor(Number(e)))===e},hh=function(e){return"[object String]"===Object.prototype.toString.call(e)},hp=function(e){return 0===l.Children.count(e)},hb=function(e){return hf(e)&&hl(e.then)};function hm(e,t,n,r){void 0===r&&(r=0);for(var i=d8(t);e&&r=0?[]:{}}}return(0===a?e:i)[o[a]]===n?e:(void 0===n?delete i[o[a]]:i[o[a]]=n,0===a&&void 0===n&&delete r[o[a]],r)}function hv(e,t,n,r){void 0===n&&(n=new WeakMap),void 0===r&&(r={});for(var i=0,a=Object.keys(e);i0?t.map(function(t){return x(t,hm(e,t))}):[Promise.resolve("DO_NOT_DELETE_YOU_WILL_BE_FIRED")]).then(function(e){return e.reduce(function(e,n,r){return"DO_NOT_DELETE_YOU_WILL_BE_FIRED"===n||n&&(e=hg(e,t[r],n)),e},{})})},[x]),M=(0,l.useCallback)(function(e){return Promise.all([T(e),h.validationSchema?k(e):{},h.validate?S(e):{}]).then(function(e){var t=e[0],n=e[1],r=e[2];return sk.all([t,n,r],{arrayMerge:hL})})},[h.validate,h.validationSchema,T,S,k]),O=hN(function(e){return void 0===e&&(e=_.values),E({type:"SET_ISVALIDATING",payload:!0}),M(e).then(function(e){return v.current&&(E({type:"SET_ISVALIDATING",payload:!1}),sd()(_.errors,e)||E({type:"SET_ERRORS",payload:e})),e})});(0,l.useEffect)(function(){o&&!0===v.current&&sd()(p.current,h.initialValues)&&O(p.current)},[o,O]);var A=(0,l.useCallback)(function(e){var t=e&&e.values?e.values:p.current,n=e&&e.errors?e.errors:b.current?b.current:h.initialErrors||{},r=e&&e.touched?e.touched:m.current?m.current:h.initialTouched||{},i=e&&e.status?e.status:g.current?g.current:h.initialStatus;p.current=t,b.current=n,m.current=r,g.current=i;var a=function(){E({type:"RESET_FORM",payload:{isSubmitting:!!e&&!!e.isSubmitting,errors:n,touched:r,status:i,values:t,isValidating:!!e&&!!e.isValidating,submitCount:e&&e.submitCount&&"number"==typeof e.submitCount?e.submitCount:0}})};if(h.onReset){var o=h.onReset(_.values,V);hb(o)?o.then(a):a()}else a()},[h.initialErrors,h.initialStatus,h.initialTouched]);(0,l.useEffect)(function(){!0===v.current&&!sd()(p.current,h.initialValues)&&(c&&(p.current=h.initialValues,A()),o&&O(p.current))},[c,h.initialValues,A,o,O]),(0,l.useEffect)(function(){c&&!0===v.current&&!sd()(b.current,h.initialErrors)&&(b.current=h.initialErrors||hS,E({type:"SET_ERRORS",payload:h.initialErrors||hS}))},[c,h.initialErrors]),(0,l.useEffect)(function(){c&&!0===v.current&&!sd()(m.current,h.initialTouched)&&(m.current=h.initialTouched||hk,E({type:"SET_TOUCHED",payload:h.initialTouched||hk}))},[c,h.initialTouched]),(0,l.useEffect)(function(){c&&!0===v.current&&!sd()(g.current,h.initialStatus)&&(g.current=h.initialStatus,E({type:"SET_STATUS",payload:h.initialStatus}))},[c,h.initialStatus,h.initialTouched]);var L=hN(function(e){if(y.current[e]&&hl(y.current[e].validate)){var t=hm(_.values,e),n=y.current[e].validate(t);return hb(n)?(E({type:"SET_ISVALIDATING",payload:!0}),n.then(function(e){return e}).then(function(t){E({type:"SET_FIELD_ERROR",payload:{field:e,value:t}}),E({type:"SET_ISVALIDATING",payload:!1})})):(E({type:"SET_FIELD_ERROR",payload:{field:e,value:n}}),Promise.resolve(n))}return h.validationSchema?(E({type:"SET_ISVALIDATING",payload:!0}),k(_.values,e).then(function(e){return e}).then(function(t){E({type:"SET_FIELD_ERROR",payload:{field:e,value:t[e]}}),E({type:"SET_ISVALIDATING",payload:!1})})):Promise.resolve()}),C=(0,l.useCallback)(function(e,t){var n=t.validate;y.current[e]={validate:n}},[]),I=(0,l.useCallback)(function(e){delete y.current[e]},[]),D=hN(function(e,t){return E({type:"SET_TOUCHED",payload:e}),(void 0===t?i:t)?O(_.values):Promise.resolve()}),N=(0,l.useCallback)(function(e){E({type:"SET_ERRORS",payload:e})},[]),P=hN(function(e,t){var r=hl(e)?e(_.values):e;return E({type:"SET_VALUES",payload:r}),(void 0===t?n:t)?O(r):Promise.resolve()}),R=(0,l.useCallback)(function(e,t){E({type:"SET_FIELD_ERROR",payload:{field:e,value:t}})},[]),j=hN(function(e,t,r){return E({type:"SET_FIELD_VALUE",payload:{field:e,value:t}}),(void 0===r?n:r)?O(hg(_.values,e,t)):Promise.resolve()}),F=(0,l.useCallback)(function(e,t){var n,r=t,i=e;if(!hh(e)){e.persist&&e.persist();var a=e.target?e.target:e.currentTarget,o=a.type,s=a.name,u=a.id,c=a.value,l=a.checked,f=(a.outerHTML,a.options),d=a.multiple;r=t||s||u,i=/number|range/.test(o)?(n=parseFloat(c),isNaN(n)?"":n):/checkbox/.test(o)?hI(hm(_.values,r),l,c):d?hC(f):c}r&&j(r,i)},[j,_.values]),Y=hN(function(e){if(hh(e))return function(t){return F(t,e)};F(e)}),B=hN(function(e,t,n){return void 0===t&&(t=!0),E({type:"SET_FIELD_TOUCHED",payload:{field:e,value:t}}),(void 0===n?i:n)?O(_.values):Promise.resolve()}),U=(0,l.useCallback)(function(e,t){e.persist&&e.persist();var n,r=e.target,i=r.name,a=r.id;r.outerHTML,B(t||i||a,!0)},[B]),H=hN(function(e){if(hh(e))return function(t){return U(t,e)};U(e)}),$=(0,l.useCallback)(function(e){hl(e)?E({type:"SET_FORMIK_STATE",payload:e}):E({type:"SET_FORMIK_STATE",payload:function(){return e}})},[]),z=(0,l.useCallback)(function(e){E({type:"SET_STATUS",payload:e})},[]),G=(0,l.useCallback)(function(e){E({type:"SET_ISSUBMITTING",payload:e})},[]),W=hN(function(){return E({type:"SUBMIT_ATTEMPT"}),O().then(function(e){var t,n=e instanceof Error;if(!n&&0===Object.keys(e).length){try{if(void 0===(t=q()))return}catch(r){throw r}return Promise.resolve(t).then(function(e){return v.current&&E({type:"SUBMIT_SUCCESS"}),e}).catch(function(e){if(v.current)throw E({type:"SUBMIT_FAILURE"}),e})}if(v.current&&(E({type:"SUBMIT_FAILURE"}),n))throw e})}),K=hN(function(e){e&&e.preventDefault&&hl(e.preventDefault)&&e.preventDefault(),e&&e.stopPropagation&&hl(e.stopPropagation)&&e.stopPropagation(),W().catch(function(e){console.warn("Warning: An unhandled error was caught from submitForm()",e)})}),V={resetForm:A,validateForm:O,validateField:L,setErrors:N,setFieldError:R,setFieldTouched:B,setFieldValue:j,setStatus:z,setSubmitting:G,setTouched:D,setValues:P,setFormikState:$,submitForm:W},q=hN(function(){return f(_.values,V)}),Z=hN(function(e){e&&e.preventDefault&&hl(e.preventDefault)&&e.preventDefault(),e&&e.stopPropagation&&hl(e.stopPropagation)&&e.stopPropagation(),A()}),X=(0,l.useCallback)(function(e){return{value:hm(_.values,e),error:hm(_.errors,e),touched:!!hm(_.touched,e),initialValue:hm(p.current,e),initialTouched:!!hm(m.current,e),initialError:hm(b.current,e)}},[_.errors,_.touched,_.values]),J=(0,l.useCallback)(function(e){return{setValue:function(t,n){return j(e,t,n)},setTouched:function(t,n){return B(e,t,n)},setError:function(t){return R(e,t)}}},[j,B,R]),Q=(0,l.useCallback)(function(e){var t=hf(e),n=t?e.name:e,r=hm(_.values,n),i={name:n,value:r,onChange:Y,onBlur:H};if(t){var a=e.type,o=e.value,s=e.as,u=e.multiple;"checkbox"===a?void 0===o?i.checked=!!r:(i.checked=!!(Array.isArray(r)&&~r.indexOf(o)),i.value=o):"radio"===a?(i.checked=r===o,i.value=o):"select"===s&&u&&(i.value=i.value||[],i.multiple=!0)}return i},[H,Y,_.values]),ee=(0,l.useMemo)(function(){return!sd()(p.current,_.values)},[p.current,_.values]),et=(0,l.useMemo)(function(){return void 0!==s?ee?_.errors&&0===Object.keys(_.errors).length:!1!==s&&hl(s)?s(h):s:_.errors&&0===Object.keys(_.errors).length},[s,ee,_.errors,h]);return ha({},_,{initialValues:p.current,initialErrors:b.current,initialTouched:m.current,initialStatus:g.current,handleBlur:H,handleChange:Y,handleReset:Z,handleSubmit:K,resetForm:A,setErrors:N,setFormikState:$,setFieldTouched:B,setFieldValue:j,setFieldError:R,setStatus:z,setSubmitting:G,setTouched:D,setValues:P,submitForm:W,validateForm:O,validateField:L,isValid:et,dirty:ee,unregisterField:I,registerField:C,getFieldProps:Q,getFieldMeta:X,getFieldHelpers:J,validateOnBlur:i,validateOnChange:n,validateOnMount:o})}function hT(e){var t=hx(e),n=e.component,r=e.children,i=e.render,a=e.innerRef;return(0,l.useImperativeHandle)(a,function(){return t}),(0,l.createElement)(hw,{value:t},n?(0,l.createElement)(n,t):i?i(t):r?hl(r)?r(t):hp(r)?null:l.Children.only(r):null)}function hM(e){var t={};if(e.inner){if(0===e.inner.length)return hg(t,e.path,e.message);for(var n=e.inner,r=Array.isArray(n),i=0,n=r?n:n[Symbol.iterator]();;){if(r){if(i>=n.length)break;a=n[i++]}else{if((i=n.next()).done)break;a=i.value}var a,o=a;hm(t,o.path)||(t=hg(t,o.path,o.message))}}return t}function hO(e,t,n,r){void 0===n&&(n=!1),void 0===r&&(r={});var i=hA(e);return t[n?"validateSync":"validate"](i,{abortEarly:!1,context:r})}function hA(e){var t=Array.isArray(e)?[]:{};for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=String(n);!0===Array.isArray(e[r])?t[r]=e[r].map(function(e){return!0===Array.isArray(e)||sR(e)?hA(e):""!==e?e:void 0}):sR(e[r])?t[r]=hA(e[r]):t[r]=""!==e[r]?e[r]:void 0}return t}function hL(e,t,n){var r=e.slice();return t.forEach(function(t,i){if(void 0===r[i]){var a=!1!==n.clone&&n.isMergeableObject(t);r[i]=a?sk(Array.isArray(t)?[]:{},t,n):t}else n.isMergeableObject(t)?r[i]=sk(e[i],t,n):-1===e.indexOf(t)&&r.push(t)}),r}function hC(e){return Array.from(e).filter(function(e){return e.selected}).map(function(e){return e.value})}function hI(e,t,n){if("boolean"==typeof e)return Boolean(t);var r=[],i=!1,a=-1;if(Array.isArray(e))r=e,i=(a=e.indexOf(n))>=0;else if(!n||"true"==n||"false"==n)return Boolean(t);return t&&n&&!i?r.concat(n):i?r.slice(0,a).concat(r.slice(a+1)):r}var hD="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement?l.useLayoutEffect:l.useEffect;function hN(e){var t=(0,l.useRef)(e);return hD(function(){t.current=e}),(0,l.useCallback)(function(){for(var e=arguments.length,n=Array(e),r=0;re?t:e},0);return Array.from(ha({},e,{length:t+1}))};(function(e){function t(t){var n;return(n=e.call(this,t)||this).updateArrayField=function(e,t,r){var i=n.props,a=i.name;(0,i.formik.setFormikState)(function(n){var i="function"==typeof r?r:e,o="function"==typeof t?t:e,s=hg(n.values,a,e(hm(n.values,a))),u=r?i(hm(n.errors,a)):void 0,c=t?o(hm(n.touched,a)):void 0;return hc(u)&&(u=void 0),hc(c)&&(c=void 0),ha({},n,{values:s,errors:r?hg(n.errors,a,u):n.errors,touched:t?hg(n.touched,a,c):n.touched})})},n.push=function(e){return n.updateArrayField(function(t){return[].concat(hU(t),[hi(e)])},!1,!1)},n.handlePush=function(e){return function(){return n.push(e)}},n.swap=function(e,t){return n.updateArrayField(function(n){return hF(n,e,t)},!0,!0)},n.handleSwap=function(e,t){return function(){return n.swap(e,t)}},n.move=function(e,t){return n.updateArrayField(function(n){return hj(n,e,t)},!0,!0)},n.handleMove=function(e,t){return function(){return n.move(e,t)}},n.insert=function(e,t){return n.updateArrayField(function(n){return hY(n,e,t)},function(t){return hY(t,e,null)},function(t){return hY(t,e,null)})},n.handleInsert=function(e,t){return function(){return n.insert(e,t)}},n.replace=function(e,t){return n.updateArrayField(function(n){return hB(n,e,t)},!1,!1)},n.handleReplace=function(e,t){return function(){return n.replace(e,t)}},n.unshift=function(e){var t=-1;return n.updateArrayField(function(n){var r=n?[e].concat(n):[e];return t<0&&(t=r.length),r},function(e){var n=e?[null].concat(e):[null];return t<0&&(t=n.length),n},function(e){var n=e?[null].concat(e):[null];return t<0&&(t=n.length),n}),t},n.handleUnshift=function(e){return function(){return n.unshift(e)}},n.handleRemove=function(e){return function(){return n.remove(e)}},n.handlePop=function(){return function(){return n.pop()}},n.remove=n.remove.bind(hu(n)),n.pop=n.pop.bind(hu(n)),n}ho(t,e);var n=t.prototype;return n.componentDidUpdate=function(e){this.props.validateOnChange&&this.props.formik.validateOnChange&&!sd()(hm(e.formik.values,e.name),hm(this.props.formik.values,this.props.name))&&this.props.formik.validateForm(this.props.formik.values)},n.remove=function(e){var t;return this.updateArrayField(function(n){var r=n?hU(n):[];return t||(t=r[e]),hl(r.splice)&&r.splice(e,1),r},!0,!0),t},n.pop=function(){var e;return this.updateArrayField(function(t){var n=t;return e||(e=n&&n.pop&&n.pop()),n},!0,!0),e},n.render=function(){var e={push:this.push,pop:this.pop,swap:this.swap,move:this.move,insert:this.insert,replace:this.replace,unshift:this.unshift,remove:this.remove,handlePush:this.handlePush,handlePop:this.handlePop,handleSwap:this.handleSwap,handleMove:this.handleMove,handleInsert:this.handleInsert,handleReplace:this.handleReplace,handleUnshift:this.handleUnshift,handleRemove:this.handleRemove},t=this.props,n=t.component,r=t.render,i=t.children,a=t.name,o=hs(t.formik,["validate","validationSchema"]),s=ha({},e,{form:o,name:a});return n?(0,l.createElement)(n,s):r?r(s):i?"function"==typeof i?i(s):hp(i)?null:l.Children.only(i):null},t})(l.Component).defaultProps={validateOnChange:!0},l.Component,l.Component;var hH=n(24802),h$=n(71209),hz=n(91750),hG=n(11970),hW=n(4689),hK=n(67598),hV=function(){return(hV=Object.assign||function(e){for(var t,n=1,r=arguments.length;nt.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&(n[r[i]]=e[r[i]]);return n}function hZ(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hq(n,["onBlur"]),a=e.form,o=a.isSubmitting,s=a.touched,u=a.errors,c=e.onBlur,l=e.helperText,f=hq(e,["disabled","field","form","onBlur","helperText"]),d=hm(u,i.name),h=hm(s,i.name)&&!!d;return hV(hV({variant:f.variant,error:h,helperText:h?d:l,disabled:null!=t?t:o,onBlur:null!=c?c:function(e){r(null!=e?e:i.name)}},i),f)}function hX(e){var t=e.children,n=hq(e,["children"]);return(0,l.createElement)(iw.Z,hV({},hZ(n)),t)}function hJ(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hq(n,["onBlur"]),a=e.form.isSubmitting,o=(e.type,e.onBlur),s=hq(e,["disabled","field","form","type","onBlur"]);return hV(hV({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function hQ(e){return(0,l.createElement)(hH.Z,hV({},hJ(e)))}function h1(e){var t,n=e.disabled,r=e.field,i=r.onBlur,a=hq(r,["onBlur"]),o=e.form.isSubmitting,s=(e.type,e.onBlur),u=hq(e,["disabled","field","form","type","onBlur"]);return hV(hV({disabled:null!=n?n:o,indeterminate:!Array.isArray(a.value)&&null==a.value,onBlur:null!=s?s:function(e){i(null!=e?e:a.name)}},a),u)}function h0(e){return(0,l.createElement)(h$.Z,hV({},h1(e)))}function h2(e){var t=e.Label,n=hq(e,["Label"]);return(0,l.createElement)(hz.Z,hV({control:(0,l.createElement)(h$.Z,hV({},h1(n)))},t))}function h3(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hq(n,["onBlur"]),a=e.form.isSubmitting,o=e.onBlur,s=hq(e,["disabled","field","form","onBlur"]);return hV(hV({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function h4(e){return(0,l.createElement)(hG.default,hV({},h3(e)))}function h6(e){var t=e.field,n=t.onBlur,r=hq(t,["onBlur"]),i=(e.form,e.onBlur),a=hq(e,["field","form","onBlur"]);return hV(hV({onBlur:null!=i?i:function(e){n(null!=e?e:r.name)}},r),a)}function h5(e){return(0,l.createElement)(hW.Z,hV({},h6(e)))}function h8(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hq(n,["onBlur"]),a=e.form.isSubmitting,o=e.onBlur,s=hq(e,["disabled","field","form","onBlur"]);return hV(hV({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function h9(e){return(0,l.createElement)(hK.default,hV({},h8(e)))}hX.displayName="FormikMaterialUITextField",hQ.displayName="FormikMaterialUISwitch",h0.displayName="FormikMaterialUICheckbox",h2.displayName="FormikMaterialUICheckboxWithLabel",h4.displayName="FormikMaterialUISelect",h5.displayName="FormikMaterialUIRadioGroup",h9.displayName="FormikMaterialUIInputBase";try{a=Map}catch(h7){}try{o=Set}catch(pe){}function pt(e,t,n){if(!e||"object"!=typeof e||"function"==typeof e)return e;if(e.nodeType&&"cloneNode"in e)return e.cloneNode(!0);if(e instanceof Date)return new Date(e.getTime());if(e instanceof RegExp)return RegExp(e);if(Array.isArray(e))return e.map(pn);if(a&&e instanceof a)return new Map(Array.from(e.entries()));if(o&&e instanceof o)return new Set(Array.from(e.values()));if(e instanceof Object){t.push(e);var r=Object.create(e);for(var i in n.push(r),e){var s=t.findIndex(function(t){return t===e[i]});r[i]=s>-1?n[s]:pt(e[i],t,n)}return r}return e}function pn(e){return pt(e,[],[])}let pr=Object.prototype.toString,pi=Error.prototype.toString,pa=RegExp.prototype.toString,po="undefined"!=typeof Symbol?Symbol.prototype.toString:()=>"",ps=/^Symbol\((.*)\)(.*)$/;function pu(e){if(e!=+e)return"NaN";let t=0===e&&1/e<0;return t?"-0":""+e}function pc(e,t=!1){if(null==e||!0===e||!1===e)return""+e;let n=typeof e;if("number"===n)return pu(e);if("string"===n)return t?`"${e}"`:e;if("function"===n)return"[Function "+(e.name||"anonymous")+"]";if("symbol"===n)return po.call(e).replace(ps,"Symbol($1)");let r=pr.call(e).slice(8,-1);return"Date"===r?isNaN(e.getTime())?""+e:e.toISOString(e):"Error"===r||e instanceof Error?"["+pi.call(e)+"]":"RegExp"===r?pa.call(e):null}function pl(e,t){let n=pc(e,t);return null!==n?n:JSON.stringify(e,function(e,n){let r=pc(this[e],t);return null!==r?r:n},2)}let pf={default:"${path} is invalid",required:"${path} is a required field",oneOf:"${path} must be one of the following values: ${values}",notOneOf:"${path} must not be one of the following values: ${values}",notType({path:e,type:t,value:n,originalValue:r}){let i=null!=r&&r!==n,a=`${e} must be a \`${t}\` type, but the final value was: \`${pl(n,!0)}\``+(i?` (cast from the value \`${pl(r,!0)}\`).`:".");return null===n&&(a+='\n If "null" is intended as an empty value be sure to mark the schema as `.nullable()`'),a},defined:"${path} must be defined"},pd={length:"${path} must be exactly ${length} characters",min:"${path} must be at least ${min} characters",max:"${path} must be at most ${max} characters",matches:'${path} must match the following: "${regex}"',email:"${path} must be a valid email",url:"${path} must be a valid URL",uuid:"${path} must be a valid UUID",trim:"${path} must be a trimmed string",lowercase:"${path} must be a lowercase string",uppercase:"${path} must be a upper case string"},ph={min:"${path} must be greater than or equal to ${min}",max:"${path} must be less than or equal to ${max}",lessThan:"${path} must be less than ${less}",moreThan:"${path} must be greater than ${more}",positive:"${path} must be a positive number",negative:"${path} must be a negative number",integer:"${path} must be an integer"},pp={min:"${path} field must be later than ${min}",max:"${path} field must be at earlier than ${max}"},pb={isValue:"${path} field must be ${value}"},pm={noUnknown:"${path} field has unspecified keys: ${unknown}"},pg={min:"${path} field must have at least ${min} items",max:"${path} field must have less than or equal to ${max} items",length:"${path} must be have ${length} items"};Object.assign(Object.create(null),{mixed:pf,string:pd,number:ph,date:pp,object:pm,array:pg,boolean:pb});var pv=n(18721),py=n.n(pv);let pw=e=>e&&e.__isYupSchema__;class p_{constructor(e,t){if(this.refs=e,this.refs=e,"function"==typeof t){this.fn=t;return}if(!py()(t,"is"))throw TypeError("`is:` is required for `when()` conditions");if(!t.then&&!t.otherwise)throw TypeError("either `then:` or `otherwise:` is required for `when()` conditions");let{is:n,then:r,otherwise:i}=t,a="function"==typeof n?n:(...e)=>e.every(e=>e===n);this.fn=function(...e){let t=e.pop(),n=e.pop(),o=a(...e)?r:i;if(o)return"function"==typeof o?o(n):n.concat(o.resolve(t))}}resolve(e,t){let n=this.refs.map(e=>e.getValue(null==t?void 0:t.value,null==t?void 0:t.parent,null==t?void 0:t.context)),r=this.fn.apply(e,n.concat(e,t));if(void 0===r||r===e)return e;if(!pw(r))throw TypeError("conditions must return a schema object");return r.resolve(t)}}let pE=p_;function pS(e){return null==e?[]:[].concat(e)}function pk(){return(pk=Object.assign||function(e){for(var t=1;tpl(t[n])):"function"==typeof e?e(t):e}static isError(e){return e&&"ValidationError"===e.name}constructor(e,t,n,r){super(),this.name="ValidationError",this.value=t,this.path=n,this.type=r,this.errors=[],this.inner=[],pS(e).forEach(e=>{pT.isError(e)?(this.errors.push(...e.errors),this.inner=this.inner.concat(e.inner.length?e.inner:e)):this.errors.push(e)}),this.message=this.errors.length>1?`${this.errors.length} errors occurred`:this.errors[0],Error.captureStackTrace&&Error.captureStackTrace(this,pT)}}let pM=e=>{let t=!1;return(...n)=>{t||(t=!0,e(...n))}};function pO(e,t){let{endEarly:n,tests:r,args:i,value:a,errors:o,sort:s,path:u}=e,c=pM(t),l=r.length,f=[];if(o=o||[],!l)return o.length?c(new pT(o,a,u)):c(null,a);for(let d=0;d=0||(i[n]=e[n]);return i}function pR(e){function t(t,n){let{value:r,path:i="",label:a,options:o,originalValue:s,sync:u}=t,c=pP(t,["value","path","label","options","originalValue","sync"]),{name:l,test:f,params:d,message:h}=e,{parent:p,context:b}=o;function m(e){return pD.isRef(e)?e.getValue(r,p,b):e}function g(e={}){let t=pL()(pN({value:r,originalValue:s,label:a,path:e.path||i},d,e.params),m),n=new pT(pT.formatError(e.message||h,t),r,t.path,e.type||l);return n.params=t,n}let v=pN({path:i,parent:p,type:l,createError:g,resolve:m,options:o,originalValue:s},c);if(!u){try{Promise.resolve(f.call(v,r,v)).then(e=>{pT.isError(e)?n(e):e?n(null,e):n(g())})}catch(y){n(y)}return}let w;try{var _;if(w=f.call(v,r,v),"function"==typeof(null==(_=w)?void 0:_.then))throw Error(`Validation test of type: "${v.type}" returned a Promise during a synchronous validate. This test will finish after the validate call has returned`)}catch(E){n(E);return}pT.isError(w)?n(w):w?n(null,w):n(g())}return t.OPTIONS=e,t}pD.prototype.__isYupRef=!0;let pj=e=>e.substr(0,e.length-1).substr(1);function pF(e,t,n,r=n){let i,a,o;return t?((0,pC.forEach)(t,(s,u,c)=>{let l=u?pj(s):s;if((e=e.resolve({context:r,parent:i,value:n})).innerType){let f=c?parseInt(l,10):0;if(n&&f>=n.length)throw Error(`Yup.reach cannot resolve an array item at index: ${s}, in the path: ${t}. because there is no value at that index. `);i=n,n=n&&n[f],e=e.innerType}if(!c){if(!e.fields||!e.fields[l])throw Error(`The schema does not contain the path: ${t}. (failed at: ${o} which is a type: "${e._type}")`);i=n,n=n&&n[l],e=e.fields[l]}a=l,o=u?"["+s+"]":"."+s}),{schema:e,parent:i,parentPath:a}):{parent:i,parentPath:t,schema:e}}class pY{constructor(){this.list=new Set,this.refs=new Map}get size(){return this.list.size+this.refs.size}describe(){let e=[];for(let t of this.list)e.push(t);for(let[,n]of this.refs)e.push(n.describe());return e}toArray(){return Array.from(this.list).concat(Array.from(this.refs.values()))}add(e){pD.isRef(e)?this.refs.set(e.key,e):this.list.add(e)}delete(e){pD.isRef(e)?this.refs.delete(e.key):this.list.delete(e)}has(e,t){if(this.list.has(e))return!0;let n,r=this.refs.values();for(;!(n=r.next()).done;)if(t(n.value)===e)return!0;return!1}clone(){let e=new pY;return e.list=new Set(this.list),e.refs=new Map(this.refs),e}merge(e,t){let n=this.clone();return e.list.forEach(e=>n.add(e)),e.refs.forEach(e=>n.add(e)),t.list.forEach(e=>n.delete(e)),t.refs.forEach(e=>n.delete(e)),n}}function pB(){return(pB=Object.assign||function(e){for(var t=1;t{this.typeError(pf.notType)}),this.type=(null==e?void 0:e.type)||"mixed",this.spec=pB({strip:!1,strict:!1,abortEarly:!0,recursive:!0,nullable:!1,presence:"optional"},null==e?void 0:e.spec)}get _type(){return this.type}_typeCheck(e){return!0}clone(e){if(this._mutate)return e&&Object.assign(this.spec,e),this;let t=Object.create(Object.getPrototypeOf(this));return t.type=this.type,t._typeError=this._typeError,t._whitelistError=this._whitelistError,t._blacklistError=this._blacklistError,t._whitelist=this._whitelist.clone(),t._blacklist=this._blacklist.clone(),t.exclusiveTests=pB({},this.exclusiveTests),t.deps=[...this.deps],t.conditions=[...this.conditions],t.tests=[...this.tests],t.transforms=[...this.transforms],t.spec=pn(pB({},this.spec,e)),t}label(e){var t=this.clone();return t.spec.label=e,t}meta(...e){if(0===e.length)return this.spec.meta;let t=this.clone();return t.spec.meta=Object.assign(t.spec.meta||{},e[0]),t}withMutation(e){let t=this._mutate;this._mutate=!0;let n=e(this);return this._mutate=t,n}concat(e){if(!e||e===this)return this;if(e.type!==this.type&&"mixed"!==this.type)throw TypeError(`You cannot \`concat()\` schema's of different types: ${this.type} and ${e.type}`);let t=this,n=e.clone(),r=pB({},t.spec,n.spec);return n.spec=r,n._typeError||(n._typeError=t._typeError),n._whitelistError||(n._whitelistError=t._whitelistError),n._blacklistError||(n._blacklistError=t._blacklistError),n._whitelist=t._whitelist.merge(e._whitelist,e._blacklist),n._blacklist=t._blacklist.merge(e._blacklist,e._whitelist),n.tests=t.tests,n.exclusiveTests=t.exclusiveTests,n.withMutation(t=>{e.tests.forEach(e=>{t.test(e.OPTIONS)})}),n}isType(e){return!!this.spec.nullable&&null===e||this._typeCheck(e)}resolve(e){let t=this;if(t.conditions.length){let n=t.conditions;(t=t.clone()).conditions=[],t=(t=n.reduce((t,n)=>n.resolve(t,e),t)).resolve(e)}return t}cast(e,t={}){let n=this.resolve(pB({value:e},t)),r=n._cast(e,t);if(void 0!==e&&!1!==t.assert&&!0!==n.isType(r)){let i=pl(e),a=pl(r);throw TypeError(`The value of ${t.path||"field"} could not be cast to a value that satisfies the schema type: "${n._type}". + */ Object.defineProperty(t,"__esModule",{value:!0}),"undefined"==typeof window||"function"!=typeof MessageChannel){var n,r,i,a,o,s=null,u=null,c=function(){if(null!==s)try{var e=t.unstable_now();s(!0,e),s=null}catch(n){throw setTimeout(c,0),n}},l=Date.now();t.unstable_now=function(){return Date.now()-l},n=function(e){null!==s?setTimeout(n,0,e):(s=e,setTimeout(c,0))},r=function(e,t){u=setTimeout(e,t)},i=function(){clearTimeout(u)},a=function(){return!1},o=t.unstable_forceFrameRate=function(){}}else{var f=window.performance,d=window.Date,h=window.setTimeout,p=window.clearTimeout;if("undefined"!=typeof console){var b=window.cancelAnimationFrame;"function"!=typeof window.requestAnimationFrame&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"),"function"!=typeof b&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills")}if("object"==typeof f&&"function"==typeof f.now)t.unstable_now=function(){return f.now()};else{var m=d.now();t.unstable_now=function(){return d.now()-m}}var g=!1,v=null,y=-1,w=5,_=0;a=function(){return t.unstable_now()>=_},o=function(){},t.unstable_forceFrameRate=function(e){0>e||125M(o,n))void 0!==u&&0>M(u,o)?(e[r]=u,e[s]=n,r=s):(e[r]=o,e[a]=n,r=a);else if(void 0!==u&&0>M(u,n))e[r]=u,e[s]=n,r=s;else break a}}return t}return null}function M(e,t){var n=e.sortIndex-t.sortIndex;return 0!==n?n:e.id-t.id}var O=[],A=[],L=1,C=null,I=3,D=!1,N=!1,P=!1;function R(e){for(var t=x(A);null!==t;){if(null===t.callback)T(A);else if(t.startTime<=e)T(A),t.sortIndex=t.expirationTime,k(O,t);else break;t=x(A)}}function j(e){if(P=!1,R(e),!N){if(null!==x(O))N=!0,n(F);else{var t=x(A);null!==t&&r(j,t.startTime-e)}}}function F(e,n){N=!1,P&&(P=!1,i()),D=!0;var o=I;try{for(R(n),C=x(O);null!==C&&(!(C.expirationTime>n)||e&&!a());){var s=C.callback;if(null!==s){C.callback=null,I=C.priorityLevel;var u=s(C.expirationTime<=n);n=t.unstable_now(),"function"==typeof u?C.callback=u:C===x(O)&&T(O),R(n)}else T(O);C=x(O)}if(null!==C)var c=!0;else{var l=x(A);null!==l&&r(j,l.startTime-n),c=!1}return c}finally{C=null,I=o,D=!1}}function Y(e){switch(e){case 1:return -1;case 2:return 250;case 5:return 1073741823;case 4:return 1e4;default:return 5e3}}var B=o;t.unstable_ImmediatePriority=1,t.unstable_UserBlockingPriority=2,t.unstable_NormalPriority=3,t.unstable_IdlePriority=5,t.unstable_LowPriority=4,t.unstable_runWithPriority=function(e,t){switch(e){case 1:case 2:case 3:case 4:case 5:break;default:e=3}var n=I;I=e;try{return t()}finally{I=n}},t.unstable_next=function(e){switch(I){case 1:case 2:case 3:var t=3;break;default:t=I}var n=I;I=t;try{return e()}finally{I=n}},t.unstable_scheduleCallback=function(e,a,o){var s=t.unstable_now();if("object"==typeof o&&null!==o){var u=o.delay;u="number"==typeof u&&0s?(e.sortIndex=u,k(A,e),null===x(O)&&e===x(A)&&(P?i():P=!0,r(j,u-s))):(e.sortIndex=o,k(O,e),N||D||(N=!0,n(F))),e},t.unstable_cancelCallback=function(e){e.callback=null},t.unstable_wrapCallback=function(e){var t=I;return function(){var n=I;I=t;try{return e.apply(this,arguments)}finally{I=n}}},t.unstable_getCurrentPriorityLevel=function(){return I},t.unstable_shouldYield=function(){var e=t.unstable_now();R(e);var n=x(O);return n!==C&&null!==C&&null!==n&&null!==n.callback&&n.startTime<=e&&n.expirationTime>5==6?2:e>>4==14?3:e>>3==30?4:e>>6==2?-1:-2}function c(e,t,n){var r=t.length-1;if(r=0?(i>0&&(e.lastNeed=i-1),i):--r=0?(i>0&&(e.lastNeed=i-2),i):--r=0?(i>0&&(2===i?i=0:e.lastNeed=i-3),i):0}function l(e,t,n){if((192&t[0])!=128)return e.lastNeed=0,"�";if(e.lastNeed>1&&t.length>1){if((192&t[1])!=128)return e.lastNeed=1,"�";if(e.lastNeed>2&&t.length>2&&(192&t[2])!=128)return e.lastNeed=2,"�"}}function f(e){var t=this.lastTotal-this.lastNeed,n=l(this,e,t);return void 0!==n?n:this.lastNeed<=e.length?(e.copy(this.lastChar,t,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):void(e.copy(this.lastChar,t,0,e.length),this.lastNeed-=e.length)}function d(e,t){var n=c(this,e,t);if(!this.lastNeed)return e.toString("utf8",t);this.lastTotal=n;var r=e.length-(n-this.lastNeed);return e.copy(this.lastChar,0,r),e.toString("utf8",t,r)}function h(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+"�":t}function p(e,t){if((e.length-t)%2==0){var n=e.toString("utf16le",t);if(n){var r=n.charCodeAt(n.length-1);if(r>=55296&&r<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1],n.slice(0,-1)}return n}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=e[e.length-1],e.toString("utf16le",t,e.length-1)}function b(e){var t=e&&e.length?this.write(e):"";if(this.lastNeed){var n=this.lastTotal-this.lastNeed;return t+this.lastChar.toString("utf16le",0,n)}return t}function m(e,t){var n=(e.length-t)%3;return 0===n?e.toString("base64",t):(this.lastNeed=3-n,this.lastTotal=3,1===n?this.lastChar[0]=e[e.length-1]:(this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1]),e.toString("base64",t,e.length-n))}function g(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+this.lastChar.toString("base64",0,3-this.lastNeed):t}function v(e){return e.toString(this.encoding)}function y(e){return e&&e.length?this.write(e):""}t.s=s,s.prototype.write=function(e){var t,n;if(0===e.length)return"";if(this.lastNeed){if(void 0===(t=this.fillLast(e)))return"";n=this.lastNeed,this.lastNeed=0}else n=0;return n */ var r=n(48764),i=r.Buffer;function a(e,t){for(var n in e)t[n]=e[n]}function o(e,t,n){return i(e,t,n)}i.from&&i.alloc&&i.allocUnsafe&&i.allocUnsafeSlow?e.exports=r:(a(r,t),t.Buffer=o),o.prototype=Object.create(i.prototype),a(i,o),o.from=function(e,t,n){if("number"==typeof e)throw TypeError("Argument must not be a number");return i(e,t,n)},o.alloc=function(e,t,n){if("number"!=typeof e)throw TypeError("Argument must be a number");var r=i(e);return void 0!==t?"string"==typeof n?r.fill(t,n):r.fill(t):r.fill(0),r},o.allocUnsafe=function(e){if("number"!=typeof e)throw TypeError("Argument must be a number");return i(e)},o.allocUnsafeSlow=function(e){if("number"!=typeof e)throw TypeError("Argument must be a number");return r.SlowBuffer(e)}},93379(e,t,n){"use strict";var r,i,a=function(){return void 0===r&&(r=Boolean(window&&document&&document.all&&!window.atob)),r},o=(i={},function(e){if(void 0===i[e]){var t=document.querySelector(e);if(window.HTMLIFrameElement&&t instanceof window.HTMLIFrameElement)try{t=t.contentDocument.head}catch(n){t=null}i[e]=t}return i[e]}),s=[];function u(e){for(var t=-1,n=0;nO2});var r,i,a,o,s,u,c,l=n(67294),f=n.t(l,2),d=n(39814),h=n(5977),p=n(57209),b=n(32316),m=n(95880),g=n(17051),v=n(71381),y=n(81701),w=n(3022),_=n(60323),E=n(87591),S=n(25649),k=n(28902),x=n(71426),T=n(48884),M=n(94184),O=n.n(M),A=n(37703),L=n(73935),C=function(){if("undefined"!=typeof Map)return Map;function e(e,t){var n=-1;return e.some(function(e,r){return e[0]===t&&(n=r,!0)}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(t){var n=e(this.__entries__,t),r=this.__entries__[n];return r&&r[1]},t.prototype.set=function(t,n){var r=e(this.__entries__,t);~r?this.__entries__[r][1]=n:this.__entries__.push([t,n])},t.prototype.delete=function(t){var n=this.__entries__,r=e(n,t);~r&&n.splice(r,1)},t.prototype.has=function(t){return!!~e(this.__entries__,t)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(e,t){void 0===t&&(t=null);for(var n=0,r=this.__entries__;n0},e.prototype.connect_=function(){I&&!this.connected_&&(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),Y?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){I&&this.connected_&&(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(e){var t=e.propertyName,n=void 0===t?"":t;F.some(function(e){return!!~n.indexOf(e)})&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),U=function(e,t){for(var n=0,r=Object.keys(t);n0},e}(),er="undefined"!=typeof WeakMap?new WeakMap:new C,ei=function(){function e(t){if(!(this instanceof e))throw TypeError("Cannot call a class as a function.");if(!arguments.length)throw TypeError("1 argument required, but only 0 present.");var n=B.getInstance(),r=new en(t,n,this);er.set(this,r)}return e}();["observe","unobserve","disconnect"].forEach(function(e){ei.prototype[e]=function(){var t;return(t=er.get(this))[e].apply(t,arguments)}});var ea=void 0!==D.ResizeObserver?D.ResizeObserver:ei;let eo=ea;var es=function(e){var t=[],n=null,r=function(){for(var r=arguments.length,i=Array(r),a=0;a=t||n<0||f&&r>=a}function g(){var e=eb();if(m(e))return v(e);s=setTimeout(g,b(e))}function v(e){return(s=void 0,d&&r)?h(e):(r=i=void 0,o)}function y(){void 0!==s&&clearTimeout(s),c=0,r=u=i=s=void 0}function w(){return void 0===s?o:v(eb())}function _(){var e=eb(),n=m(e);if(r=arguments,i=this,u=e,n){if(void 0===s)return p(u);if(f)return clearTimeout(s),s=setTimeout(g,t),h(u)}return void 0===s&&(s=setTimeout(g,t)),o}return t=ez(t)||0,ed(n)&&(l=!!n.leading,a=(f="maxWait"in n)?eW(ez(n.maxWait)||0,t):a,d="trailing"in n?!!n.trailing:d),_.cancel=y,_.flush=w,_}let eq=eV;var eZ="Expected a function";function eX(e,t,n){var r=!0,i=!0;if("function"!=typeof e)throw TypeError(eZ);return ed(n)&&(r="leading"in n?!!n.leading:r,i="trailing"in n?!!n.trailing:i),eq(e,t,{leading:r,maxWait:t,trailing:i})}let eJ=eX;var eQ={debounce:eq,throttle:eJ},e1=function(e){return eQ[e]},e0=function(e){return"function"==typeof e},e2=function(){return"undefined"==typeof window},e3=function(e){return e instanceof Element||e instanceof HTMLDocument};function e4(e){return(e4="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function e6(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}function e5(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&l.createElement(tG.Z,{variant:"indeterminate",classes:r}))};tK.propTypes={fetchCount:el().number.isRequired};let tV=(0,b.withStyles)(tW)(tK);var tq=n(5536);let tZ=n.p+"ba8bbf16ebf8e1d05bef.svg";function tX(){return(tX=Object.assign||function(e){for(var t=1;t120){for(var d=Math.floor(u/80),h=u%80,p=[],b=0;b0},name:{enumerable:!1},nodes:{enumerable:!1},source:{enumerable:!1},positions:{enumerable:!1},originalError:{enumerable:!1}}),null!=s&&s.stack)?(Object.defineProperty(nf(b),"stack",{value:s.stack,writable:!0,configurable:!0}),nl(b)):(Error.captureStackTrace?Error.captureStackTrace(nf(b),n):Object.defineProperty(nf(b),"stack",{value:Error().stack,writable:!0,configurable:!0}),b)}return ns(n,[{key:"toString",value:function(){return nw(this)}},{key:t4.YF,get:function(){return"Object"}}]),n}(nd(Error));function ny(e){return void 0===e||0===e.length?void 0:e}function nw(e){var t=e.message;if(e.nodes)for(var n=0,r=e.nodes;n",EOF:"",BANG:"!",DOLLAR:"$",AMP:"&",PAREN_L:"(",PAREN_R:")",SPREAD:"...",COLON:":",EQUALS:"=",AT:"@",BRACKET_L:"[",BRACKET_R:"]",BRACE_L:"{",PIPE:"|",BRACE_R:"}",NAME:"Name",INT:"Int",FLOAT:"Float",STRING:"String",BLOCK_STRING:"BlockString",COMMENT:"Comment"}),nx=n(10143),nT=Object.freeze({QUERY:"QUERY",MUTATION:"MUTATION",SUBSCRIPTION:"SUBSCRIPTION",FIELD:"FIELD",FRAGMENT_DEFINITION:"FRAGMENT_DEFINITION",FRAGMENT_SPREAD:"FRAGMENT_SPREAD",INLINE_FRAGMENT:"INLINE_FRAGMENT",VARIABLE_DEFINITION:"VARIABLE_DEFINITION",SCHEMA:"SCHEMA",SCALAR:"SCALAR",OBJECT:"OBJECT",FIELD_DEFINITION:"FIELD_DEFINITION",ARGUMENT_DEFINITION:"ARGUMENT_DEFINITION",INTERFACE:"INTERFACE",UNION:"UNION",ENUM:"ENUM",ENUM_VALUE:"ENUM_VALUE",INPUT_OBJECT:"INPUT_OBJECT",INPUT_FIELD_DEFINITION:"INPUT_FIELD_DEFINITION"}),nM=n(87392),nO=function(){function e(e){var t=new nS.WU(nk.SOF,0,0,0,0,null);this.source=e,this.lastToken=t,this.token=t,this.line=1,this.lineStart=0}var t=e.prototype;return t.advance=function(){return this.lastToken=this.token,this.token=this.lookahead()},t.lookahead=function(){var e,t=this.token;if(t.kind!==nk.EOF)do t=null!==(e=t.next)&&void 0!==e?e:t.next=nC(this,t);while(t.kind===nk.COMMENT)return t},e}();function nA(e){return e===nk.BANG||e===nk.DOLLAR||e===nk.AMP||e===nk.PAREN_L||e===nk.PAREN_R||e===nk.SPREAD||e===nk.COLON||e===nk.EQUALS||e===nk.AT||e===nk.BRACKET_L||e===nk.BRACKET_R||e===nk.BRACE_L||e===nk.PIPE||e===nk.BRACE_R}function nL(e){return isNaN(e)?nk.EOF:e<127?JSON.stringify(String.fromCharCode(e)):'"\\u'.concat(("00"+e.toString(16).toUpperCase()).slice(-4),'"')}function nC(e,t){for(var n=e.source,r=n.body,i=r.length,a=t.end;a31||9===a))return new nS.WU(nk.COMMENT,t,s,n,r,i,o.slice(t+1,s))}function nN(e,t,n,r,i,a){var o=e.body,s=n,u=t,c=!1;if(45===s&&(s=o.charCodeAt(++u)),48===s){if((s=o.charCodeAt(++u))>=48&&s<=57)throw n_(e,u,"Invalid number, unexpected digit after 0: ".concat(nL(s),"."))}else u=nP(e,u,s),s=o.charCodeAt(u);if(46===s&&(c=!0,s=o.charCodeAt(++u),u=nP(e,u,s),s=o.charCodeAt(u)),(69===s||101===s)&&(c=!0,(43===(s=o.charCodeAt(++u))||45===s)&&(s=o.charCodeAt(++u)),u=nP(e,u,s),s=o.charCodeAt(u)),46===s||nU(s))throw n_(e,u,"Invalid number, expected digit but got: ".concat(nL(s),"."));return new nS.WU(c?nk.FLOAT:nk.INT,t,u,r,i,a,o.slice(t,u))}function nP(e,t,n){var r=e.body,i=t,a=n;if(a>=48&&a<=57){do a=r.charCodeAt(++i);while(a>=48&&a<=57)return i}throw n_(e,i,"Invalid number, expected digit but got: ".concat(nL(a),"."))}function nR(e,t,n,r,i){for(var a=e.body,o=t+1,s=o,u=0,c="";o=48&&e<=57?e-48:e>=65&&e<=70?e-55:e>=97&&e<=102?e-87:-1}function nB(e,t,n,r,i){for(var a=e.body,o=a.length,s=t+1,u=0;s!==o&&!isNaN(u=a.charCodeAt(s))&&(95===u||u>=48&&u<=57||u>=65&&u<=90||u>=97&&u<=122);)++s;return new nS.WU(nk.NAME,t,s,n,r,i,a.slice(t,s))}function nU(e){return 95===e||e>=65&&e<=90||e>=97&&e<=122}function nH(e,t){return new n$(e,t).parseDocument()}var n$=function(){function e(e,t){var n=(0,nx.T)(e)?e:new nx.H(e);this._lexer=new nO(n),this._options=t}var t=e.prototype;return t.parseName=function(){var e=this.expectToken(nk.NAME);return{kind:nE.h.NAME,value:e.value,loc:this.loc(e)}},t.parseDocument=function(){var e=this._lexer.token;return{kind:nE.h.DOCUMENT,definitions:this.many(nk.SOF,this.parseDefinition,nk.EOF),loc:this.loc(e)}},t.parseDefinition=function(){if(this.peek(nk.NAME))switch(this._lexer.token.value){case"query":case"mutation":case"subscription":return this.parseOperationDefinition();case"fragment":return this.parseFragmentDefinition();case"schema":case"scalar":case"type":case"interface":case"union":case"enum":case"input":case"directive":return this.parseTypeSystemDefinition();case"extend":return this.parseTypeSystemExtension()}else if(this.peek(nk.BRACE_L))return this.parseOperationDefinition();else if(this.peekDescription())return this.parseTypeSystemDefinition();throw this.unexpected()},t.parseOperationDefinition=function(){var e,t=this._lexer.token;if(this.peek(nk.BRACE_L))return{kind:nE.h.OPERATION_DEFINITION,operation:"query",name:void 0,variableDefinitions:[],directives:[],selectionSet:this.parseSelectionSet(),loc:this.loc(t)};var n=this.parseOperationType();return this.peek(nk.NAME)&&(e=this.parseName()),{kind:nE.h.OPERATION_DEFINITION,operation:n,name:e,variableDefinitions:this.parseVariableDefinitions(),directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(t)}},t.parseOperationType=function(){var e=this.expectToken(nk.NAME);switch(e.value){case"query":return"query";case"mutation":return"mutation";case"subscription":return"subscription"}throw this.unexpected(e)},t.parseVariableDefinitions=function(){return this.optionalMany(nk.PAREN_L,this.parseVariableDefinition,nk.PAREN_R)},t.parseVariableDefinition=function(){var e=this._lexer.token;return{kind:nE.h.VARIABLE_DEFINITION,variable:this.parseVariable(),type:(this.expectToken(nk.COLON),this.parseTypeReference()),defaultValue:this.expectOptionalToken(nk.EQUALS)?this.parseValueLiteral(!0):void 0,directives:this.parseDirectives(!0),loc:this.loc(e)}},t.parseVariable=function(){var e=this._lexer.token;return this.expectToken(nk.DOLLAR),{kind:nE.h.VARIABLE,name:this.parseName(),loc:this.loc(e)}},t.parseSelectionSet=function(){var e=this._lexer.token;return{kind:nE.h.SELECTION_SET,selections:this.many(nk.BRACE_L,this.parseSelection,nk.BRACE_R),loc:this.loc(e)}},t.parseSelection=function(){return this.peek(nk.SPREAD)?this.parseFragment():this.parseField()},t.parseField=function(){var e,t,n=this._lexer.token,r=this.parseName();return this.expectOptionalToken(nk.COLON)?(e=r,t=this.parseName()):t=r,{kind:nE.h.FIELD,alias:e,name:t,arguments:this.parseArguments(!1),directives:this.parseDirectives(!1),selectionSet:this.peek(nk.BRACE_L)?this.parseSelectionSet():void 0,loc:this.loc(n)}},t.parseArguments=function(e){var t=e?this.parseConstArgument:this.parseArgument;return this.optionalMany(nk.PAREN_L,t,nk.PAREN_R)},t.parseArgument=function(){var e=this._lexer.token,t=this.parseName();return this.expectToken(nk.COLON),{kind:nE.h.ARGUMENT,name:t,value:this.parseValueLiteral(!1),loc:this.loc(e)}},t.parseConstArgument=function(){var e=this._lexer.token;return{kind:nE.h.ARGUMENT,name:this.parseName(),value:(this.expectToken(nk.COLON),this.parseValueLiteral(!0)),loc:this.loc(e)}},t.parseFragment=function(){var e=this._lexer.token;this.expectToken(nk.SPREAD);var t=this.expectOptionalKeyword("on");return!t&&this.peek(nk.NAME)?{kind:nE.h.FRAGMENT_SPREAD,name:this.parseFragmentName(),directives:this.parseDirectives(!1),loc:this.loc(e)}:{kind:nE.h.INLINE_FRAGMENT,typeCondition:t?this.parseNamedType():void 0,directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(e)}},t.parseFragmentDefinition=function(){var e,t=this._lexer.token;return(this.expectKeyword("fragment"),(null===(e=this._options)||void 0===e?void 0:e.experimentalFragmentVariables)===!0)?{kind:nE.h.FRAGMENT_DEFINITION,name:this.parseFragmentName(),variableDefinitions:this.parseVariableDefinitions(),typeCondition:(this.expectKeyword("on"),this.parseNamedType()),directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(t)}:{kind:nE.h.FRAGMENT_DEFINITION,name:this.parseFragmentName(),typeCondition:(this.expectKeyword("on"),this.parseNamedType()),directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(t)}},t.parseFragmentName=function(){if("on"===this._lexer.token.value)throw this.unexpected();return this.parseName()},t.parseValueLiteral=function(e){var t=this._lexer.token;switch(t.kind){case nk.BRACKET_L:return this.parseList(e);case nk.BRACE_L:return this.parseObject(e);case nk.INT:return this._lexer.advance(),{kind:nE.h.INT,value:t.value,loc:this.loc(t)};case nk.FLOAT:return this._lexer.advance(),{kind:nE.h.FLOAT,value:t.value,loc:this.loc(t)};case nk.STRING:case nk.BLOCK_STRING:return this.parseStringLiteral();case nk.NAME:switch(this._lexer.advance(),t.value){case"true":return{kind:nE.h.BOOLEAN,value:!0,loc:this.loc(t)};case"false":return{kind:nE.h.BOOLEAN,value:!1,loc:this.loc(t)};case"null":return{kind:nE.h.NULL,loc:this.loc(t)};default:return{kind:nE.h.ENUM,value:t.value,loc:this.loc(t)}}case nk.DOLLAR:if(!e)return this.parseVariable()}throw this.unexpected()},t.parseStringLiteral=function(){var e=this._lexer.token;return this._lexer.advance(),{kind:nE.h.STRING,value:e.value,block:e.kind===nk.BLOCK_STRING,loc:this.loc(e)}},t.parseList=function(e){var t=this,n=this._lexer.token,r=function(){return t.parseValueLiteral(e)};return{kind:nE.h.LIST,values:this.any(nk.BRACKET_L,r,nk.BRACKET_R),loc:this.loc(n)}},t.parseObject=function(e){var t=this,n=this._lexer.token,r=function(){return t.parseObjectField(e)};return{kind:nE.h.OBJECT,fields:this.any(nk.BRACE_L,r,nk.BRACE_R),loc:this.loc(n)}},t.parseObjectField=function(e){var t=this._lexer.token,n=this.parseName();return this.expectToken(nk.COLON),{kind:nE.h.OBJECT_FIELD,name:n,value:this.parseValueLiteral(e),loc:this.loc(t)}},t.parseDirectives=function(e){for(var t=[];this.peek(nk.AT);)t.push(this.parseDirective(e));return t},t.parseDirective=function(e){var t=this._lexer.token;return this.expectToken(nk.AT),{kind:nE.h.DIRECTIVE,name:this.parseName(),arguments:this.parseArguments(e),loc:this.loc(t)}},t.parseTypeReference=function(){var e,t=this._lexer.token;return(this.expectOptionalToken(nk.BRACKET_L)?(e=this.parseTypeReference(),this.expectToken(nk.BRACKET_R),e={kind:nE.h.LIST_TYPE,type:e,loc:this.loc(t)}):e=this.parseNamedType(),this.expectOptionalToken(nk.BANG))?{kind:nE.h.NON_NULL_TYPE,type:e,loc:this.loc(t)}:e},t.parseNamedType=function(){var e=this._lexer.token;return{kind:nE.h.NAMED_TYPE,name:this.parseName(),loc:this.loc(e)}},t.parseTypeSystemDefinition=function(){var e=this.peekDescription()?this._lexer.lookahead():this._lexer.token;if(e.kind===nk.NAME)switch(e.value){case"schema":return this.parseSchemaDefinition();case"scalar":return this.parseScalarTypeDefinition();case"type":return this.parseObjectTypeDefinition();case"interface":return this.parseInterfaceTypeDefinition();case"union":return this.parseUnionTypeDefinition();case"enum":return this.parseEnumTypeDefinition();case"input":return this.parseInputObjectTypeDefinition();case"directive":return this.parseDirectiveDefinition()}throw this.unexpected(e)},t.peekDescription=function(){return this.peek(nk.STRING)||this.peek(nk.BLOCK_STRING)},t.parseDescription=function(){if(this.peekDescription())return this.parseStringLiteral()},t.parseSchemaDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("schema");var n=this.parseDirectives(!0),r=this.many(nk.BRACE_L,this.parseOperationTypeDefinition,nk.BRACE_R);return{kind:nE.h.SCHEMA_DEFINITION,description:t,directives:n,operationTypes:r,loc:this.loc(e)}},t.parseOperationTypeDefinition=function(){var e=this._lexer.token,t=this.parseOperationType();this.expectToken(nk.COLON);var n=this.parseNamedType();return{kind:nE.h.OPERATION_TYPE_DEFINITION,operation:t,type:n,loc:this.loc(e)}},t.parseScalarTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("scalar");var n=this.parseName(),r=this.parseDirectives(!0);return{kind:nE.h.SCALAR_TYPE_DEFINITION,description:t,name:n,directives:r,loc:this.loc(e)}},t.parseObjectTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("type");var n=this.parseName(),r=this.parseImplementsInterfaces(),i=this.parseDirectives(!0),a=this.parseFieldsDefinition();return{kind:nE.h.OBJECT_TYPE_DEFINITION,description:t,name:n,interfaces:r,directives:i,fields:a,loc:this.loc(e)}},t.parseImplementsInterfaces=function(){var e;if(!this.expectOptionalKeyword("implements"))return[];if((null===(e=this._options)||void 0===e?void 0:e.allowLegacySDLImplementsInterfaces)===!0){var t=[];this.expectOptionalToken(nk.AMP);do t.push(this.parseNamedType());while(this.expectOptionalToken(nk.AMP)||this.peek(nk.NAME))return t}return this.delimitedMany(nk.AMP,this.parseNamedType)},t.parseFieldsDefinition=function(){var e;return(null===(e=this._options)||void 0===e?void 0:e.allowLegacySDLEmptyFields)===!0&&this.peek(nk.BRACE_L)&&this._lexer.lookahead().kind===nk.BRACE_R?(this._lexer.advance(),this._lexer.advance(),[]):this.optionalMany(nk.BRACE_L,this.parseFieldDefinition,nk.BRACE_R)},t.parseFieldDefinition=function(){var e=this._lexer.token,t=this.parseDescription(),n=this.parseName(),r=this.parseArgumentDefs();this.expectToken(nk.COLON);var i=this.parseTypeReference(),a=this.parseDirectives(!0);return{kind:nE.h.FIELD_DEFINITION,description:t,name:n,arguments:r,type:i,directives:a,loc:this.loc(e)}},t.parseArgumentDefs=function(){return this.optionalMany(nk.PAREN_L,this.parseInputValueDef,nk.PAREN_R)},t.parseInputValueDef=function(){var e,t=this._lexer.token,n=this.parseDescription(),r=this.parseName();this.expectToken(nk.COLON);var i=this.parseTypeReference();this.expectOptionalToken(nk.EQUALS)&&(e=this.parseValueLiteral(!0));var a=this.parseDirectives(!0);return{kind:nE.h.INPUT_VALUE_DEFINITION,description:n,name:r,type:i,defaultValue:e,directives:a,loc:this.loc(t)}},t.parseInterfaceTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("interface");var n=this.parseName(),r=this.parseImplementsInterfaces(),i=this.parseDirectives(!0),a=this.parseFieldsDefinition();return{kind:nE.h.INTERFACE_TYPE_DEFINITION,description:t,name:n,interfaces:r,directives:i,fields:a,loc:this.loc(e)}},t.parseUnionTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("union");var n=this.parseName(),r=this.parseDirectives(!0),i=this.parseUnionMemberTypes();return{kind:nE.h.UNION_TYPE_DEFINITION,description:t,name:n,directives:r,types:i,loc:this.loc(e)}},t.parseUnionMemberTypes=function(){return this.expectOptionalToken(nk.EQUALS)?this.delimitedMany(nk.PIPE,this.parseNamedType):[]},t.parseEnumTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("enum");var n=this.parseName(),r=this.parseDirectives(!0),i=this.parseEnumValuesDefinition();return{kind:nE.h.ENUM_TYPE_DEFINITION,description:t,name:n,directives:r,values:i,loc:this.loc(e)}},t.parseEnumValuesDefinition=function(){return this.optionalMany(nk.BRACE_L,this.parseEnumValueDefinition,nk.BRACE_R)},t.parseEnumValueDefinition=function(){var e=this._lexer.token,t=this.parseDescription(),n=this.parseName(),r=this.parseDirectives(!0);return{kind:nE.h.ENUM_VALUE_DEFINITION,description:t,name:n,directives:r,loc:this.loc(e)}},t.parseInputObjectTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("input");var n=this.parseName(),r=this.parseDirectives(!0),i=this.parseInputFieldsDefinition();return{kind:nE.h.INPUT_OBJECT_TYPE_DEFINITION,description:t,name:n,directives:r,fields:i,loc:this.loc(e)}},t.parseInputFieldsDefinition=function(){return this.optionalMany(nk.BRACE_L,this.parseInputValueDef,nk.BRACE_R)},t.parseTypeSystemExtension=function(){var e=this._lexer.lookahead();if(e.kind===nk.NAME)switch(e.value){case"schema":return this.parseSchemaExtension();case"scalar":return this.parseScalarTypeExtension();case"type":return this.parseObjectTypeExtension();case"interface":return this.parseInterfaceTypeExtension();case"union":return this.parseUnionTypeExtension();case"enum":return this.parseEnumTypeExtension();case"input":return this.parseInputObjectTypeExtension()}throw this.unexpected(e)},t.parseSchemaExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("schema");var t=this.parseDirectives(!0),n=this.optionalMany(nk.BRACE_L,this.parseOperationTypeDefinition,nk.BRACE_R);if(0===t.length&&0===n.length)throw this.unexpected();return{kind:nE.h.SCHEMA_EXTENSION,directives:t,operationTypes:n,loc:this.loc(e)}},t.parseScalarTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("scalar");var t=this.parseName(),n=this.parseDirectives(!0);if(0===n.length)throw this.unexpected();return{kind:nE.h.SCALAR_TYPE_EXTENSION,name:t,directives:n,loc:this.loc(e)}},t.parseObjectTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("type");var t=this.parseName(),n=this.parseImplementsInterfaces(),r=this.parseDirectives(!0),i=this.parseFieldsDefinition();if(0===n.length&&0===r.length&&0===i.length)throw this.unexpected();return{kind:nE.h.OBJECT_TYPE_EXTENSION,name:t,interfaces:n,directives:r,fields:i,loc:this.loc(e)}},t.parseInterfaceTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("interface");var t=this.parseName(),n=this.parseImplementsInterfaces(),r=this.parseDirectives(!0),i=this.parseFieldsDefinition();if(0===n.length&&0===r.length&&0===i.length)throw this.unexpected();return{kind:nE.h.INTERFACE_TYPE_EXTENSION,name:t,interfaces:n,directives:r,fields:i,loc:this.loc(e)}},t.parseUnionTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("union");var t=this.parseName(),n=this.parseDirectives(!0),r=this.parseUnionMemberTypes();if(0===n.length&&0===r.length)throw this.unexpected();return{kind:nE.h.UNION_TYPE_EXTENSION,name:t,directives:n,types:r,loc:this.loc(e)}},t.parseEnumTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("enum");var t=this.parseName(),n=this.parseDirectives(!0),r=this.parseEnumValuesDefinition();if(0===n.length&&0===r.length)throw this.unexpected();return{kind:nE.h.ENUM_TYPE_EXTENSION,name:t,directives:n,values:r,loc:this.loc(e)}},t.parseInputObjectTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("input");var t=this.parseName(),n=this.parseDirectives(!0),r=this.parseInputFieldsDefinition();if(0===n.length&&0===r.length)throw this.unexpected();return{kind:nE.h.INPUT_OBJECT_TYPE_EXTENSION,name:t,directives:n,fields:r,loc:this.loc(e)}},t.parseDirectiveDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("directive"),this.expectToken(nk.AT);var n=this.parseName(),r=this.parseArgumentDefs(),i=this.expectOptionalKeyword("repeatable");this.expectKeyword("on");var a=this.parseDirectiveLocations();return{kind:nE.h.DIRECTIVE_DEFINITION,description:t,name:n,arguments:r,repeatable:i,locations:a,loc:this.loc(e)}},t.parseDirectiveLocations=function(){return this.delimitedMany(nk.PIPE,this.parseDirectiveLocation)},t.parseDirectiveLocation=function(){var e=this._lexer.token,t=this.parseName();if(void 0!==nT[t.value])return t;throw this.unexpected(e)},t.loc=function(e){var t;if((null===(t=this._options)||void 0===t?void 0:t.noLocation)!==!0)return new nS.Ye(e,this._lexer.lastToken,this._lexer.source)},t.peek=function(e){return this._lexer.token.kind===e},t.expectToken=function(e){var t=this._lexer.token;if(t.kind===e)return this._lexer.advance(),t;throw n_(this._lexer.source,t.start,"Expected ".concat(nG(e),", found ").concat(nz(t),"."))},t.expectOptionalToken=function(e){var t=this._lexer.token;if(t.kind===e)return this._lexer.advance(),t},t.expectKeyword=function(e){var t=this._lexer.token;if(t.kind===nk.NAME&&t.value===e)this._lexer.advance();else throw n_(this._lexer.source,t.start,'Expected "'.concat(e,'", found ').concat(nz(t),"."))},t.expectOptionalKeyword=function(e){var t=this._lexer.token;return t.kind===nk.NAME&&t.value===e&&(this._lexer.advance(),!0)},t.unexpected=function(e){var t=null!=e?e:this._lexer.token;return n_(this._lexer.source,t.start,"Unexpected ".concat(nz(t),"."))},t.any=function(e,t,n){this.expectToken(e);for(var r=[];!this.expectOptionalToken(n);)r.push(t.call(this));return r},t.optionalMany=function(e,t,n){if(this.expectOptionalToken(e)){var r=[];do r.push(t.call(this));while(!this.expectOptionalToken(n))return r}return[]},t.many=function(e,t,n){this.expectToken(e);var r=[];do r.push(t.call(this));while(!this.expectOptionalToken(n))return r},t.delimitedMany=function(e,t){this.expectOptionalToken(e);var n=[];do n.push(t.call(this));while(this.expectOptionalToken(e))return n},e}();function nz(e){var t=e.value;return nG(e.kind)+(null!=t?' "'.concat(t,'"'):"")}function nG(e){return nA(e)?'"'.concat(e,'"'):e}var nW=new Map,nK=new Map,nV=!0,nq=!1;function nZ(e){return e.replace(/[\s,]+/g," ").trim()}function nX(e){return nZ(e.source.body.substring(e.start,e.end))}function nJ(e){var t=new Set,n=[];return e.definitions.forEach(function(e){if("FragmentDefinition"===e.kind){var r=e.name.value,i=nX(e.loc),a=nK.get(r);a&&!a.has(i)?nV&&console.warn("Warning: fragment with name "+r+" already exists.\ngraphql-tag enforces all fragment names across your application to be unique; read more about\nthis in the docs: http://dev.apollodata.com/core/fragments.html#unique-names"):a||nK.set(r,a=new Set),a.add(i),t.has(i)||(t.add(i),n.push(e))}else n.push(e)}),(0,t0.pi)((0,t0.pi)({},e),{definitions:n})}function nQ(e){var t=new Set(e.definitions);t.forEach(function(e){e.loc&&delete e.loc,Object.keys(e).forEach(function(n){var r=e[n];r&&"object"==typeof r&&t.add(r)})});var n=e.loc;return n&&(delete n.startToken,delete n.endToken),e}function n1(e){var t=nZ(e);if(!nW.has(t)){var n=nH(e,{experimentalFragmentVariables:nq,allowLegacyFragmentVariables:nq});if(!n||"Document"!==n.kind)throw Error("Not a valid GraphQL document.");nW.set(t,nQ(nJ(n)))}return nW.get(t)}function n0(e){for(var t=[],n=1;n, or pass an ApolloClient instance in via options.'):(0,n9.kG)(!!n,32),n}var rp=n(10542),rb=n(53712),rm=n(21436),rg=Object.prototype.hasOwnProperty;function rv(e,t){return void 0===t&&(t=Object.create(null)),ry(rh(t.client),e).useQuery(t)}function ry(e,t){var n=(0,l.useRef)();n.current&&e===n.current.client&&t===n.current.query||(n.current=new rw(e,t,n.current));var r=n.current,i=(0,l.useState)(0),a=(i[0],i[1]);return r.forceUpdate=function(){a(function(e){return e+1})},r}var rw=function(){function e(e,t,n){this.client=e,this.query=t,this.ssrDisabledResult=(0,rp.J)({loading:!0,data:void 0,error:void 0,networkStatus:ru.I.loading}),this.skipStandbyResult=(0,rp.J)({loading:!1,data:void 0,error:void 0,networkStatus:ru.I.ready}),this.toQueryResultCache=new(n7.mr?WeakMap:Map),rd(t,r.Query);var i=n&&n.result,a=i&&i.data;a&&(this.previousData=a)}return e.prototype.forceUpdate=function(){__DEV__&&n9.kG.warn("Calling default no-op implementation of InternalState#forceUpdate")},e.prototype.executeQuery=function(e){var t,n=this;e.query&&Object.assign(this,{query:e.query}),this.watchQueryOptions=this.createWatchQueryOptions(this.queryHookOptions=e);var r=this.observable.reobserveAsConcast(this.getObsQueryOptions());return this.previousData=(null===(t=this.result)||void 0===t?void 0:t.data)||this.previousData,this.result=void 0,this.forceUpdate(),new Promise(function(e){var t;r.subscribe({next:function(e){t=e},error:function(){e(n.toQueryResult(n.observable.getCurrentResult()))},complete:function(){e(n.toQueryResult(t))}})})},e.prototype.useQuery=function(e){var t=this;this.renderPromises=(0,l.useContext)((0,ro.K)()).renderPromises,this.useOptions(e);var n=this.useObservableQuery(),r=rt((0,l.useCallback)(function(){if(t.renderPromises)return function(){};var e=function(){var e=t.result,r=n.getCurrentResult();!(e&&e.loading===r.loading&&e.networkStatus===r.networkStatus&&(0,ri.D)(e.data,r.data))&&t.setResult(r)},r=function(a){var o=n.last;i.unsubscribe();try{n.resetLastResults(),i=n.subscribe(e,r)}finally{n.last=o}if(!rg.call(a,"graphQLErrors"))throw a;var s=t.result;(!s||s&&s.loading||!(0,ri.D)(a,s.error))&&t.setResult({data:s&&s.data,error:a,loading:!1,networkStatus:ru.I.error})},i=n.subscribe(e,r);return function(){return setTimeout(function(){return i.unsubscribe()})}},[n,this.renderPromises,this.client.disableNetworkFetches,]),function(){return t.getCurrentResult()},function(){return t.getCurrentResult()});return this.unsafeHandlePartialRefetch(r),this.toQueryResult(r)},e.prototype.useOptions=function(t){var n,r=this.createWatchQueryOptions(this.queryHookOptions=t),i=this.watchQueryOptions;!(0,ri.D)(r,i)&&(this.watchQueryOptions=r,i&&this.observable&&(this.observable.reobserve(this.getObsQueryOptions()),this.previousData=(null===(n=this.result)||void 0===n?void 0:n.data)||this.previousData,this.result=void 0)),this.onCompleted=t.onCompleted||e.prototype.onCompleted,this.onError=t.onError||e.prototype.onError,(this.renderPromises||this.client.disableNetworkFetches)&&!1===this.queryHookOptions.ssr&&!this.queryHookOptions.skip?this.result=this.ssrDisabledResult:this.queryHookOptions.skip||"standby"===this.watchQueryOptions.fetchPolicy?this.result=this.skipStandbyResult:(this.result===this.ssrDisabledResult||this.result===this.skipStandbyResult)&&(this.result=void 0)},e.prototype.getObsQueryOptions=function(){var e=[],t=this.client.defaultOptions.watchQuery;return t&&e.push(t),this.queryHookOptions.defaultOptions&&e.push(this.queryHookOptions.defaultOptions),e.push((0,rb.o)(this.observable&&this.observable.options,this.watchQueryOptions)),e.reduce(ra.J)},e.prototype.createWatchQueryOptions=function(e){void 0===e&&(e={});var t,n=e.skip,r=Object.assign((e.ssr,e.onCompleted,e.onError,e.defaultOptions,(0,t0._T)(e,["skip","ssr","onCompleted","onError","defaultOptions"])),{query:this.query});if(this.renderPromises&&("network-only"===r.fetchPolicy||"cache-and-network"===r.fetchPolicy)&&(r.fetchPolicy="cache-first"),r.variables||(r.variables={}),n){var i=r.fetchPolicy,a=void 0===i?this.getDefaultFetchPolicy():i,o=r.initialFetchPolicy;Object.assign(r,{initialFetchPolicy:void 0===o?a:o,fetchPolicy:"standby"})}else r.fetchPolicy||(r.fetchPolicy=(null===(t=this.observable)||void 0===t?void 0:t.options.initialFetchPolicy)||this.getDefaultFetchPolicy());return r},e.prototype.getDefaultFetchPolicy=function(){var e,t;return(null===(e=this.queryHookOptions.defaultOptions)||void 0===e?void 0:e.fetchPolicy)||(null===(t=this.client.defaultOptions.watchQuery)||void 0===t?void 0:t.fetchPolicy)||"cache-first"},e.prototype.onCompleted=function(e){},e.prototype.onError=function(e){},e.prototype.useObservableQuery=function(){var e=this.observable=this.renderPromises&&this.renderPromises.getSSRObservable(this.watchQueryOptions)||this.observable||this.client.watchQuery(this.getObsQueryOptions());this.obsQueryFields=(0,l.useMemo)(function(){return{refetch:e.refetch.bind(e),reobserve:e.reobserve.bind(e),fetchMore:e.fetchMore.bind(e),updateQuery:e.updateQuery.bind(e),startPolling:e.startPolling.bind(e),stopPolling:e.stopPolling.bind(e),subscribeToMore:e.subscribeToMore.bind(e)}},[e]);var t=!(!1===this.queryHookOptions.ssr||this.queryHookOptions.skip);return this.renderPromises&&t&&(this.renderPromises.registerSSRObservable(e),e.getCurrentResult().loading&&this.renderPromises.addObservableQueryPromise(e)),e},e.prototype.setResult=function(e){var t=this.result;t&&t.data&&(this.previousData=t.data),this.result=e,this.forceUpdate(),this.handleErrorOrCompleted(e)},e.prototype.handleErrorOrCompleted=function(e){var t=this;if(!e.loading){var n=this.toApolloError(e);Promise.resolve().then(function(){n?t.onError(n):e.data&&t.onCompleted(e.data)}).catch(function(e){__DEV__&&n9.kG.warn(e)})}},e.prototype.toApolloError=function(e){return(0,rm.O)(e.errors)?new rs.cA({graphQLErrors:e.errors}):e.error},e.prototype.getCurrentResult=function(){return this.result||this.handleErrorOrCompleted(this.result=this.observable.getCurrentResult()),this.result},e.prototype.toQueryResult=function(e){var t=this.toQueryResultCache.get(e);if(t)return t;var n=e.data,r=(e.partial,(0,t0._T)(e,["data","partial"]));return this.toQueryResultCache.set(e,t=(0,t0.pi)((0,t0.pi)((0,t0.pi)({data:n},r),this.obsQueryFields),{client:this.client,observable:this.observable,variables:this.observable.variables,called:!this.queryHookOptions.skip,previousData:this.previousData})),!t.error&&(0,rm.O)(e.errors)&&(t.error=new rs.cA({graphQLErrors:e.errors})),t},e.prototype.unsafeHandlePartialRefetch=function(e){e.partial&&this.queryHookOptions.partialRefetch&&!e.loading&&(!e.data||0===Object.keys(e.data).length)&&"cache-only"!==this.observable.options.fetchPolicy&&(Object.assign(e,{loading:!0,networkStatus:ru.I.refetch}),this.observable.refetch())},e}();function r_(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&void 0!==arguments[0]?arguments[0]:{};return rv(iH,e)},iz=function(){var e=ij(),t=parseInt(e.get("page")||"1",10),n=parseInt(e.get("per")||"50",10),r=i$({variables:{offset:(t-1)*n,limit:n},fetchPolicy:"network-only"}),i=r.data,a=r.loading,o=r.error;return a?l.createElement(iR,null):o?l.createElement(iD,{error:o}):i?l.createElement(iI,{chains:i.chains.results,page:t,pageSize:n,total:i.chains.metadata.total}):null},iG=n(67932),iW=n(8126),iK="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function iV(e){if(iq())return Intl.DateTimeFormat.supportedLocalesOf(e)[0]}function iq(){return("undefined"==typeof Intl?"undefined":iK(Intl))==="object"&&"function"==typeof Intl.DateTimeFormat}var iZ="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},iX=function(){function e(e,t){for(var n=0;n=i.length)break;s=i[o++]}else{if((o=i.next()).done)break;s=o.value}var s,u=s;if((void 0===e?"undefined":iZ(e))!=="object")return;e=e[u]}return e}},{key:"put",value:function(){for(var e=arguments.length,t=Array(e),n=0;n=o.length)break;c=o[u++]}else{if((u=o.next()).done)break;c=u.value}var c,l=c;"object"!==iZ(a[l])&&(a[l]={}),a=a[l]}return a[i]=r}}]),e}();let i1=iQ;var i0=new i1;function i2(e,t){if(!iq())return function(e){return e.toString()};var n=i4(e),r=JSON.stringify(t),i=i0.get(String(n),r)||i0.put(String(n),r,new Intl.DateTimeFormat(n,t));return function(e){return i.format(e)}}var i3={};function i4(e){var t=e.toString();return i3[t]?i3[t]:i3[t]=iV(e)}var i6="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function i5(e){return i8(e)?e:new Date(e)}function i8(e){return e instanceof Date||i9(e)}function i9(e){return(void 0===e?"undefined":i6(e))==="object"&&"function"==typeof e.getTime}var i7=n(54087),ae=n.n(i7);function at(e,t){if(0===e.length)return 0;for(var n=0,r=e.length-1,i=void 0;n<=r;){var a=t(e[i=Math.floor((r+n)/2)]);if(0===a)return i;if(a<0){if((n=i+1)>r)return n}else if((r=i-1)=t.nextUpdateTime)aa(t,this.instances);else break}},scheduleNextTick:function(){var e=this;this.scheduledTick=ae()(function(){e.tick(),e.scheduleNextTick()})},start:function(){this.scheduleNextTick()},stop:function(){ae().cancel(this.scheduledTick)}};function ai(e){var t=an(e.getNextValue(),2),n=t[0],r=t[1];e.setValue(n),e.nextUpdateTime=r}function aa(e,t){ai(e),as(t,e),ao(t,e)}function ao(e,t){var n=au(e,t);e.splice(n,0,t)}function as(e,t){var n=e.indexOf(t);e.splice(n,1)}function au(e,t){var n=t.nextUpdateTime;return at(e,function(e){return e.nextUpdateTime===n?0:e.nextUpdateTime>n?1:-1})}var ac=(0,ec.oneOfType)([(0,ec.shape)({minTime:ec.number,formatAs:ec.string.isRequired}),(0,ec.shape)({test:ec.func,formatAs:ec.string.isRequired}),(0,ec.shape)({minTime:ec.number,format:ec.func.isRequired}),(0,ec.shape)({test:ec.func,format:ec.func.isRequired})]),al=(0,ec.oneOfType)([ec.string,(0,ec.shape)({steps:(0,ec.arrayOf)(ac).isRequired,labels:(0,ec.oneOfType)([ec.string,(0,ec.arrayOf)(ec.string)]).isRequired,round:ec.string})]),af=Object.assign||function(e){for(var t=1;t=0)&&Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function ap(e){var t=e.date,n=e.future,r=e.timeStyle,i=e.round,a=e.minTimeLeft,o=e.tooltip,s=e.component,u=e.container,c=e.wrapperComponent,f=e.wrapperProps,d=e.locale,h=e.locales,p=e.formatVerboseDate,b=e.verboseDateFormat,m=e.updateInterval,g=e.tick,v=ah(e,["date","future","timeStyle","round","minTimeLeft","tooltip","component","container","wrapperComponent","wrapperProps","locale","locales","formatVerboseDate","verboseDateFormat","updateInterval","tick"]),y=(0,l.useMemo)(function(){return d&&(h=[d]),h.concat(iW.Z.getDefaultLocale())},[d,h]),w=(0,l.useMemo)(function(){return new iW.Z(y)},[y]);t=(0,l.useMemo)(function(){return i5(t)},[t]);var _=(0,l.useCallback)(function(){var e=Date.now(),o=void 0;if(n&&e>=t.getTime()&&(e=t.getTime(),o=!0),void 0!==a){var s=t.getTime()-1e3*a;e>s&&(e=s,o=!0)}var u=w.format(t,r,{getTimeToNextUpdate:!0,now:e,future:n,round:i}),c=ad(u,2),l=c[0],f=c[1];return f=o?ag:m||f||6e4,[l,e+f]},[t,n,r,m,i,a,w]),E=(0,l.useRef)();E.current=_;var S=(0,l.useMemo)(_,[]),k=ad(S,2),x=k[0],T=k[1],M=(0,l.useState)(x),O=ad(M,2),A=O[0],L=O[1],C=ad((0,l.useState)(),2),I=C[0],D=C[1],N=(0,l.useRef)();(0,l.useEffect)(function(){if(g)return N.current=ar.add({getNextValue:function(){return E.current()},setValue:L,nextUpdateTime:T}),function(){return N.current.stop()}},[g]),(0,l.useEffect)(function(){if(N.current)N.current.forceUpdate();else{var e=_(),t=ad(e,1)[0];L(t)}},[_]),(0,l.useEffect)(function(){D(!0)},[]);var P=(0,l.useMemo)(function(){if("undefined"!=typeof window)return i2(y,b)},[y,b]),R=(0,l.useMemo)(function(){if("undefined"!=typeof window)return p?p(t):P(t)},[t,p,P]),j=l.createElement(s,af({date:t,verboseDate:I?R:void 0,tooltip:o},v),A),F=c||u;return F?l.createElement(F,af({},f,{verboseDate:I?R:void 0}),j):j}ap.propTypes={date:el().oneOfType([el().instanceOf(Date),el().number]).isRequired,locale:el().string,locales:el().arrayOf(el().string),future:el().bool,timeStyle:al,round:el().string,minTimeLeft:el().number,component:el().elementType.isRequired,tooltip:el().bool.isRequired,formatVerboseDate:el().func,verboseDateFormat:el().object,updateInterval:el().oneOfType([el().number,el().arrayOf(el().shape({threshold:el().number,interval:el().number.isRequired}))]),tick:el().bool,wrapperComponent:el().func,wrapperProps:el().object},ap.defaultProps={locales:[],component:av,tooltip:!0,verboseDateFormat:{weekday:"long",day:"numeric",month:"long",year:"numeric",hour:"numeric",minute:"2-digit",second:"2-digit"},tick:!0},ap=l.memo(ap);let ab=ap;var am,ag=31536e9;function av(e){var t=e.date,n=e.verboseDate,r=e.tooltip,i=e.children,a=ah(e,["date","verboseDate","tooltip","children"]),o=(0,l.useMemo)(function(){return t.toISOString()},[t]);return l.createElement("time",af({},a,{dateTime:o,title:r?n:void 0}),i)}av.propTypes={date:el().instanceOf(Date).isRequired,verboseDate:el().string,tooltip:el().bool.isRequired,children:el().string.isRequired};var ay=n(30381),aw=n.n(ay),a_=n(31657);function aE(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function aS(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0?new rs.cA({graphQLErrors:i}):void 0;if(u===s.current.mutationId&&!c.ignoreResults){var f={called:!0,loading:!1,data:r,error:l,client:a};s.current.isMounted&&!(0,ri.D)(s.current.result,f)&&o(s.current.result=f)}var d=e.onCompleted||(null===(n=s.current.options)||void 0===n?void 0:n.onCompleted);return null==d||d(t.data,c),t}).catch(function(t){if(u===s.current.mutationId&&s.current.isMounted){var n,r={loading:!1,error:t,data:void 0,called:!0,client:a};(0,ri.D)(s.current.result,r)||o(s.current.result=r)}var i=e.onError||(null===(n=s.current.options)||void 0===n?void 0:n.onError);if(i)return i(t,c),{data:void 0,errors:t};throw t})},[]),c=(0,l.useCallback)(function(){s.current.isMounted&&o({called:!1,loading:!1,client:n})},[]);return(0,l.useEffect)(function(){return s.current.isMounted=!0,function(){s.current.isMounted=!1}},[]),[u,(0,t0.pi)({reset:c},a)]}var os=n(59067),ou=n(28428),oc=n(11186),ol=n(78513);function of(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var od=function(e){return(0,b.createStyles)({paper:{display:"flex",margin:"".concat(2.5*e.spacing.unit,"px 0"),padding:"".concat(3*e.spacing.unit,"px ").concat(3.5*e.spacing.unit,"px")},content:{flex:1,width:"100%"},actions:of({marginTop:-(1.5*e.spacing.unit),marginLeft:-(4*e.spacing.unit)},e.breakpoints.up("sm"),{marginLeft:0,marginRight:-(1.5*e.spacing.unit)}),itemBlock:{border:"1px solid rgba(224, 224, 224, 1)",borderRadius:e.shape.borderRadius,padding:2*e.spacing.unit,marginTop:e.spacing.unit},itemBlockText:{overflowWrap:"anywhere"}})},oh=(0,b.withStyles)(od)(function(e){var t=e.actions,n=e.children,r=e.classes;return l.createElement(ii.default,{className:r.paper},l.createElement("div",{className:r.content},n),t&&l.createElement("div",{className:r.actions},t))}),op=function(e){var t=e.title;return l.createElement(x.default,{variant:"subtitle2",gutterBottom:!0},t)},ob=function(e){var t=e.children,n=e.value;return l.createElement(x.default,{variant:"body1",noWrap:!0},t||n)},om=(0,b.withStyles)(od)(function(e){var t=e.children,n=e.classes,r=e.value;return l.createElement("div",{className:n.itemBlock},l.createElement(x.default,{variant:"body1",className:n.itemBlockText},t||r))});function og(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]-1}let sq=sV;function sZ(e,t){var n=this.__data__,r=sH(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this}let sX=sZ;function sJ(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t-1&&e%1==0&&e-1&&e%1==0&&e<=cC}let cD=cI;var cN="[object Arguments]",cP="[object Array]",cR="[object Boolean]",cj="[object Date]",cF="[object Error]",cY="[object Function]",cB="[object Map]",cU="[object Number]",cH="[object Object]",c$="[object RegExp]",cz="[object Set]",cG="[object String]",cW="[object WeakMap]",cK="[object ArrayBuffer]",cV="[object DataView]",cq="[object Float64Array]",cZ="[object Int8Array]",cX="[object Int16Array]",cJ="[object Int32Array]",cQ="[object Uint8Array]",c1="[object Uint8ClampedArray]",c0="[object Uint16Array]",c2="[object Uint32Array]",c3={};function c4(e){return eD(e)&&cD(e.length)&&!!c3[eC(e)]}c3["[object Float32Array]"]=c3[cq]=c3[cZ]=c3[cX]=c3[cJ]=c3[cQ]=c3[c1]=c3[c0]=c3[c2]=!0,c3[cN]=c3[cP]=c3[cK]=c3[cR]=c3[cV]=c3[cj]=c3[cF]=c3[cY]=c3[cB]=c3[cU]=c3[cH]=c3[c$]=c3[cz]=c3[cG]=c3[cW]=!1;let c6=c4;function c5(e){return function(t){return e(t)}}let c8=c5;var c9=n(79730),c7=c9.Z&&c9.Z.isTypedArray,le=c7?c8(c7):c6;let lt=le;var ln=Object.prototype.hasOwnProperty;function lr(e,t){var n=cx(e),r=!n&&cS(e),i=!n&&!r&&(0,cT.Z)(e),a=!n&&!r&&!i&<(e),o=n||r||i||a,s=o?cb(e.length,String):[],u=s.length;for(var c in e)(t||ln.call(e,c))&&!(o&&("length"==c||i&&("offset"==c||"parent"==c)||a&&("buffer"==c||"byteLength"==c||"byteOffset"==c)||cL(c,u)))&&s.push(c);return s}let li=lr;var la=Object.prototype;function lo(e){var t=e&&e.constructor;return e===("function"==typeof t&&t.prototype||la)}let ls=lo;var lu=sT(Object.keys,Object);let lc=lu;var ll=Object.prototype.hasOwnProperty;function lf(e){if(!ls(e))return lc(e);var t=[];for(var n in Object(e))ll.call(e,n)&&"constructor"!=n&&t.push(n);return t}let ld=lf;function lh(e){return null!=e&&cD(e.length)&&!ur(e)}let lp=lh;function lb(e){return lp(e)?li(e):ld(e)}let lm=lb;function lg(e,t){return e&&ch(t,lm(t),e)}let lv=lg;function ly(e){var t=[];if(null!=e)for(var n in Object(e))t.push(n);return t}let lw=ly;var l_=Object.prototype.hasOwnProperty;function lE(e){if(!ed(e))return lw(e);var t=ls(e),n=[];for(var r in e)"constructor"==r&&(t||!l_.call(e,r))||n.push(r);return n}let lS=lE;function lk(e){return lp(e)?li(e,!0):lS(e)}let lx=lk;function lT(e,t){return e&&ch(t,lx(t),e)}let lM=lT;var lO=n(42896);function lA(e,t){var n=-1,r=e.length;for(t||(t=Array(r));++n=0||(i[n]=e[n]);return i}function hu(e){if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}var hc=function(e){return Array.isArray(e)&&0===e.length},hl=function(e){return"function"==typeof e},hf=function(e){return null!==e&&"object"==typeof e},hd=function(e){return String(Math.floor(Number(e)))===e},hh=function(e){return"[object String]"===Object.prototype.toString.call(e)},hp=function(e){return 0===l.Children.count(e)},hb=function(e){return hf(e)&&hl(e.then)};function hm(e,t,n,r){void 0===r&&(r=0);for(var i=d8(t);e&&r=0?[]:{}}}return(0===a?e:i)[o[a]]===n?e:(void 0===n?delete i[o[a]]:i[o[a]]=n,0===a&&void 0===n&&delete r[o[a]],r)}function hv(e,t,n,r){void 0===n&&(n=new WeakMap),void 0===r&&(r={});for(var i=0,a=Object.keys(e);i0?t.map(function(t){return x(t,hm(e,t))}):[Promise.resolve("DO_NOT_DELETE_YOU_WILL_BE_FIRED")]).then(function(e){return e.reduce(function(e,n,r){return"DO_NOT_DELETE_YOU_WILL_BE_FIRED"===n||n&&(e=hg(e,t[r],n)),e},{})})},[x]),M=(0,l.useCallback)(function(e){return Promise.all([T(e),h.validationSchema?k(e):{},h.validate?S(e):{}]).then(function(e){var t=e[0],n=e[1],r=e[2];return sk.all([t,n,r],{arrayMerge:hL})})},[h.validate,h.validationSchema,T,S,k]),O=hN(function(e){return void 0===e&&(e=_.values),E({type:"SET_ISVALIDATING",payload:!0}),M(e).then(function(e){return v.current&&(E({type:"SET_ISVALIDATING",payload:!1}),sd()(_.errors,e)||E({type:"SET_ERRORS",payload:e})),e})});(0,l.useEffect)(function(){o&&!0===v.current&&sd()(p.current,h.initialValues)&&O(p.current)},[o,O]);var A=(0,l.useCallback)(function(e){var t=e&&e.values?e.values:p.current,n=e&&e.errors?e.errors:b.current?b.current:h.initialErrors||{},r=e&&e.touched?e.touched:m.current?m.current:h.initialTouched||{},i=e&&e.status?e.status:g.current?g.current:h.initialStatus;p.current=t,b.current=n,m.current=r,g.current=i;var a=function(){E({type:"RESET_FORM",payload:{isSubmitting:!!e&&!!e.isSubmitting,errors:n,touched:r,status:i,values:t,isValidating:!!e&&!!e.isValidating,submitCount:e&&e.submitCount&&"number"==typeof e.submitCount?e.submitCount:0}})};if(h.onReset){var o=h.onReset(_.values,V);hb(o)?o.then(a):a()}else a()},[h.initialErrors,h.initialStatus,h.initialTouched]);(0,l.useEffect)(function(){!0===v.current&&!sd()(p.current,h.initialValues)&&(c&&(p.current=h.initialValues,A()),o&&O(p.current))},[c,h.initialValues,A,o,O]),(0,l.useEffect)(function(){c&&!0===v.current&&!sd()(b.current,h.initialErrors)&&(b.current=h.initialErrors||hS,E({type:"SET_ERRORS",payload:h.initialErrors||hS}))},[c,h.initialErrors]),(0,l.useEffect)(function(){c&&!0===v.current&&!sd()(m.current,h.initialTouched)&&(m.current=h.initialTouched||hk,E({type:"SET_TOUCHED",payload:h.initialTouched||hk}))},[c,h.initialTouched]),(0,l.useEffect)(function(){c&&!0===v.current&&!sd()(g.current,h.initialStatus)&&(g.current=h.initialStatus,E({type:"SET_STATUS",payload:h.initialStatus}))},[c,h.initialStatus,h.initialTouched]);var L=hN(function(e){if(y.current[e]&&hl(y.current[e].validate)){var t=hm(_.values,e),n=y.current[e].validate(t);return hb(n)?(E({type:"SET_ISVALIDATING",payload:!0}),n.then(function(e){return e}).then(function(t){E({type:"SET_FIELD_ERROR",payload:{field:e,value:t}}),E({type:"SET_ISVALIDATING",payload:!1})})):(E({type:"SET_FIELD_ERROR",payload:{field:e,value:n}}),Promise.resolve(n))}return h.validationSchema?(E({type:"SET_ISVALIDATING",payload:!0}),k(_.values,e).then(function(e){return e}).then(function(t){E({type:"SET_FIELD_ERROR",payload:{field:e,value:t[e]}}),E({type:"SET_ISVALIDATING",payload:!1})})):Promise.resolve()}),C=(0,l.useCallback)(function(e,t){var n=t.validate;y.current[e]={validate:n}},[]),I=(0,l.useCallback)(function(e){delete y.current[e]},[]),D=hN(function(e,t){return E({type:"SET_TOUCHED",payload:e}),(void 0===t?i:t)?O(_.values):Promise.resolve()}),N=(0,l.useCallback)(function(e){E({type:"SET_ERRORS",payload:e})},[]),P=hN(function(e,t){var r=hl(e)?e(_.values):e;return E({type:"SET_VALUES",payload:r}),(void 0===t?n:t)?O(r):Promise.resolve()}),R=(0,l.useCallback)(function(e,t){E({type:"SET_FIELD_ERROR",payload:{field:e,value:t}})},[]),j=hN(function(e,t,r){return E({type:"SET_FIELD_VALUE",payload:{field:e,value:t}}),(void 0===r?n:r)?O(hg(_.values,e,t)):Promise.resolve()}),F=(0,l.useCallback)(function(e,t){var n,r=t,i=e;if(!hh(e)){e.persist&&e.persist();var a=e.target?e.target:e.currentTarget,o=a.type,s=a.name,u=a.id,c=a.value,l=a.checked,f=(a.outerHTML,a.options),d=a.multiple;r=t||s||u,i=/number|range/.test(o)?(n=parseFloat(c),isNaN(n)?"":n):/checkbox/.test(o)?hI(hm(_.values,r),l,c):d?hC(f):c}r&&j(r,i)},[j,_.values]),Y=hN(function(e){if(hh(e))return function(t){return F(t,e)};F(e)}),B=hN(function(e,t,n){return void 0===t&&(t=!0),E({type:"SET_FIELD_TOUCHED",payload:{field:e,value:t}}),(void 0===n?i:n)?O(_.values):Promise.resolve()}),U=(0,l.useCallback)(function(e,t){e.persist&&e.persist();var n,r=e.target,i=r.name,a=r.id;r.outerHTML,B(t||i||a,!0)},[B]),H=hN(function(e){if(hh(e))return function(t){return U(t,e)};U(e)}),$=(0,l.useCallback)(function(e){hl(e)?E({type:"SET_FORMIK_STATE",payload:e}):E({type:"SET_FORMIK_STATE",payload:function(){return e}})},[]),z=(0,l.useCallback)(function(e){E({type:"SET_STATUS",payload:e})},[]),G=(0,l.useCallback)(function(e){E({type:"SET_ISSUBMITTING",payload:e})},[]),W=hN(function(){return E({type:"SUBMIT_ATTEMPT"}),O().then(function(e){var t,n=e instanceof Error;if(!n&&0===Object.keys(e).length){try{if(void 0===(t=q()))return}catch(r){throw r}return Promise.resolve(t).then(function(e){return v.current&&E({type:"SUBMIT_SUCCESS"}),e}).catch(function(e){if(v.current)throw E({type:"SUBMIT_FAILURE"}),e})}if(v.current&&(E({type:"SUBMIT_FAILURE"}),n))throw e})}),K=hN(function(e){e&&e.preventDefault&&hl(e.preventDefault)&&e.preventDefault(),e&&e.stopPropagation&&hl(e.stopPropagation)&&e.stopPropagation(),W().catch(function(e){console.warn("Warning: An unhandled error was caught from submitForm()",e)})}),V={resetForm:A,validateForm:O,validateField:L,setErrors:N,setFieldError:R,setFieldTouched:B,setFieldValue:j,setStatus:z,setSubmitting:G,setTouched:D,setValues:P,setFormikState:$,submitForm:W},q=hN(function(){return f(_.values,V)}),Z=hN(function(e){e&&e.preventDefault&&hl(e.preventDefault)&&e.preventDefault(),e&&e.stopPropagation&&hl(e.stopPropagation)&&e.stopPropagation(),A()}),X=(0,l.useCallback)(function(e){return{value:hm(_.values,e),error:hm(_.errors,e),touched:!!hm(_.touched,e),initialValue:hm(p.current,e),initialTouched:!!hm(m.current,e),initialError:hm(b.current,e)}},[_.errors,_.touched,_.values]),J=(0,l.useCallback)(function(e){return{setValue:function(t,n){return j(e,t,n)},setTouched:function(t,n){return B(e,t,n)},setError:function(t){return R(e,t)}}},[j,B,R]),Q=(0,l.useCallback)(function(e){var t=hf(e),n=t?e.name:e,r=hm(_.values,n),i={name:n,value:r,onChange:Y,onBlur:H};if(t){var a=e.type,o=e.value,s=e.as,u=e.multiple;"checkbox"===a?void 0===o?i.checked=!!r:(i.checked=!!(Array.isArray(r)&&~r.indexOf(o)),i.value=o):"radio"===a?(i.checked=r===o,i.value=o):"select"===s&&u&&(i.value=i.value||[],i.multiple=!0)}return i},[H,Y,_.values]),ee=(0,l.useMemo)(function(){return!sd()(p.current,_.values)},[p.current,_.values]),et=(0,l.useMemo)(function(){return void 0!==s?ee?_.errors&&0===Object.keys(_.errors).length:!1!==s&&hl(s)?s(h):s:_.errors&&0===Object.keys(_.errors).length},[s,ee,_.errors,h]);return ha({},_,{initialValues:p.current,initialErrors:b.current,initialTouched:m.current,initialStatus:g.current,handleBlur:H,handleChange:Y,handleReset:Z,handleSubmit:K,resetForm:A,setErrors:N,setFormikState:$,setFieldTouched:B,setFieldValue:j,setFieldError:R,setStatus:z,setSubmitting:G,setTouched:D,setValues:P,submitForm:W,validateForm:O,validateField:L,isValid:et,dirty:ee,unregisterField:I,registerField:C,getFieldProps:Q,getFieldMeta:X,getFieldHelpers:J,validateOnBlur:i,validateOnChange:n,validateOnMount:o})}function hT(e){var t=hx(e),n=e.component,r=e.children,i=e.render,a=e.innerRef;return(0,l.useImperativeHandle)(a,function(){return t}),(0,l.createElement)(hw,{value:t},n?(0,l.createElement)(n,t):i?i(t):r?hl(r)?r(t):hp(r)?null:l.Children.only(r):null)}function hM(e){var t={};if(e.inner){if(0===e.inner.length)return hg(t,e.path,e.message);for(var n=e.inner,r=Array.isArray(n),i=0,n=r?n:n[Symbol.iterator]();;){if(r){if(i>=n.length)break;a=n[i++]}else{if((i=n.next()).done)break;a=i.value}var a,o=a;hm(t,o.path)||(t=hg(t,o.path,o.message))}}return t}function hO(e,t,n,r){void 0===n&&(n=!1),void 0===r&&(r={});var i=hA(e);return t[n?"validateSync":"validate"](i,{abortEarly:!1,context:r})}function hA(e){var t=Array.isArray(e)?[]:{};for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=String(n);!0===Array.isArray(e[r])?t[r]=e[r].map(function(e){return!0===Array.isArray(e)||sR(e)?hA(e):""!==e?e:void 0}):sR(e[r])?t[r]=hA(e[r]):t[r]=""!==e[r]?e[r]:void 0}return t}function hL(e,t,n){var r=e.slice();return t.forEach(function(t,i){if(void 0===r[i]){var a=!1!==n.clone&&n.isMergeableObject(t);r[i]=a?sk(Array.isArray(t)?[]:{},t,n):t}else n.isMergeableObject(t)?r[i]=sk(e[i],t,n):-1===e.indexOf(t)&&r.push(t)}),r}function hC(e){return Array.from(e).filter(function(e){return e.selected}).map(function(e){return e.value})}function hI(e,t,n){if("boolean"==typeof e)return Boolean(t);var r=[],i=!1,a=-1;if(Array.isArray(e))r=e,i=(a=e.indexOf(n))>=0;else if(!n||"true"==n||"false"==n)return Boolean(t);return t&&n&&!i?r.concat(n):i?r.slice(0,a).concat(r.slice(a+1)):r}var hD="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement?l.useLayoutEffect:l.useEffect;function hN(e){var t=(0,l.useRef)(e);return hD(function(){t.current=e}),(0,l.useCallback)(function(){for(var e=arguments.length,n=Array(e),r=0;re?t:e},0);return Array.from(ha({},e,{length:t+1}))};(function(e){function t(t){var n;return(n=e.call(this,t)||this).updateArrayField=function(e,t,r){var i=n.props,a=i.name;(0,i.formik.setFormikState)(function(n){var i="function"==typeof r?r:e,o="function"==typeof t?t:e,s=hg(n.values,a,e(hm(n.values,a))),u=r?i(hm(n.errors,a)):void 0,c=t?o(hm(n.touched,a)):void 0;return hc(u)&&(u=void 0),hc(c)&&(c=void 0),ha({},n,{values:s,errors:r?hg(n.errors,a,u):n.errors,touched:t?hg(n.touched,a,c):n.touched})})},n.push=function(e){return n.updateArrayField(function(t){return[].concat(hU(t),[hi(e)])},!1,!1)},n.handlePush=function(e){return function(){return n.push(e)}},n.swap=function(e,t){return n.updateArrayField(function(n){return hF(n,e,t)},!0,!0)},n.handleSwap=function(e,t){return function(){return n.swap(e,t)}},n.move=function(e,t){return n.updateArrayField(function(n){return hj(n,e,t)},!0,!0)},n.handleMove=function(e,t){return function(){return n.move(e,t)}},n.insert=function(e,t){return n.updateArrayField(function(n){return hY(n,e,t)},function(t){return hY(t,e,null)},function(t){return hY(t,e,null)})},n.handleInsert=function(e,t){return function(){return n.insert(e,t)}},n.replace=function(e,t){return n.updateArrayField(function(n){return hB(n,e,t)},!1,!1)},n.handleReplace=function(e,t){return function(){return n.replace(e,t)}},n.unshift=function(e){var t=-1;return n.updateArrayField(function(n){var r=n?[e].concat(n):[e];return t<0&&(t=r.length),r},function(e){var n=e?[null].concat(e):[null];return t<0&&(t=n.length),n},function(e){var n=e?[null].concat(e):[null];return t<0&&(t=n.length),n}),t},n.handleUnshift=function(e){return function(){return n.unshift(e)}},n.handleRemove=function(e){return function(){return n.remove(e)}},n.handlePop=function(){return function(){return n.pop()}},n.remove=n.remove.bind(hu(n)),n.pop=n.pop.bind(hu(n)),n}ho(t,e);var n=t.prototype;return n.componentDidUpdate=function(e){this.props.validateOnChange&&this.props.formik.validateOnChange&&!sd()(hm(e.formik.values,e.name),hm(this.props.formik.values,this.props.name))&&this.props.formik.validateForm(this.props.formik.values)},n.remove=function(e){var t;return this.updateArrayField(function(n){var r=n?hU(n):[];return t||(t=r[e]),hl(r.splice)&&r.splice(e,1),r},!0,!0),t},n.pop=function(){var e;return this.updateArrayField(function(t){var n=t;return e||(e=n&&n.pop&&n.pop()),n},!0,!0),e},n.render=function(){var e={push:this.push,pop:this.pop,swap:this.swap,move:this.move,insert:this.insert,replace:this.replace,unshift:this.unshift,remove:this.remove,handlePush:this.handlePush,handlePop:this.handlePop,handleSwap:this.handleSwap,handleMove:this.handleMove,handleInsert:this.handleInsert,handleReplace:this.handleReplace,handleUnshift:this.handleUnshift,handleRemove:this.handleRemove},t=this.props,n=t.component,r=t.render,i=t.children,a=t.name,o=hs(t.formik,["validate","validationSchema"]),s=ha({},e,{form:o,name:a});return n?(0,l.createElement)(n,s):r?r(s):i?"function"==typeof i?i(s):hp(i)?null:l.Children.only(i):null},t})(l.Component).defaultProps={validateOnChange:!0},l.Component,l.Component;var hH=n(24802),h$=n(71209),hz=n(91750),hG=n(11970),hW=n(4689),hK=n(67598),hV=function(){return(hV=Object.assign||function(e){for(var t,n=1,r=arguments.length;nt.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&(n[r[i]]=e[r[i]]);return n}function hZ(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hq(n,["onBlur"]),a=e.form,o=a.isSubmitting,s=a.touched,u=a.errors,c=e.onBlur,l=e.helperText,f=hq(e,["disabled","field","form","onBlur","helperText"]),d=hm(u,i.name),h=hm(s,i.name)&&!!d;return hV(hV({variant:f.variant,error:h,helperText:h?d:l,disabled:null!=t?t:o,onBlur:null!=c?c:function(e){r(null!=e?e:i.name)}},i),f)}function hX(e){var t=e.children,n=hq(e,["children"]);return(0,l.createElement)(iw.Z,hV({},hZ(n)),t)}function hJ(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hq(n,["onBlur"]),a=e.form.isSubmitting,o=(e.type,e.onBlur),s=hq(e,["disabled","field","form","type","onBlur"]);return hV(hV({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function hQ(e){return(0,l.createElement)(hH.Z,hV({},hJ(e)))}function h1(e){var t,n=e.disabled,r=e.field,i=r.onBlur,a=hq(r,["onBlur"]),o=e.form.isSubmitting,s=(e.type,e.onBlur),u=hq(e,["disabled","field","form","type","onBlur"]);return hV(hV({disabled:null!=n?n:o,indeterminate:!Array.isArray(a.value)&&null==a.value,onBlur:null!=s?s:function(e){i(null!=e?e:a.name)}},a),u)}function h0(e){return(0,l.createElement)(h$.Z,hV({},h1(e)))}function h2(e){var t=e.Label,n=hq(e,["Label"]);return(0,l.createElement)(hz.Z,hV({control:(0,l.createElement)(h$.Z,hV({},h1(n)))},t))}function h3(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hq(n,["onBlur"]),a=e.form.isSubmitting,o=e.onBlur,s=hq(e,["disabled","field","form","onBlur"]);return hV(hV({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function h4(e){return(0,l.createElement)(hG.default,hV({},h3(e)))}function h6(e){var t=e.field,n=t.onBlur,r=hq(t,["onBlur"]),i=(e.form,e.onBlur),a=hq(e,["field","form","onBlur"]);return hV(hV({onBlur:null!=i?i:function(e){n(null!=e?e:r.name)}},r),a)}function h5(e){return(0,l.createElement)(hW.Z,hV({},h6(e)))}function h8(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hq(n,["onBlur"]),a=e.form.isSubmitting,o=e.onBlur,s=hq(e,["disabled","field","form","onBlur"]);return hV(hV({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function h9(e){return(0,l.createElement)(hK.default,hV({},h8(e)))}hX.displayName="FormikMaterialUITextField",hQ.displayName="FormikMaterialUISwitch",h0.displayName="FormikMaterialUICheckbox",h2.displayName="FormikMaterialUICheckboxWithLabel",h4.displayName="FormikMaterialUISelect",h5.displayName="FormikMaterialUIRadioGroup",h9.displayName="FormikMaterialUIInputBase";try{a=Map}catch(h7){}try{o=Set}catch(pe){}function pt(e,t,n){if(!e||"object"!=typeof e||"function"==typeof e)return e;if(e.nodeType&&"cloneNode"in e)return e.cloneNode(!0);if(e instanceof Date)return new Date(e.getTime());if(e instanceof RegExp)return RegExp(e);if(Array.isArray(e))return e.map(pn);if(a&&e instanceof a)return new Map(Array.from(e.entries()));if(o&&e instanceof o)return new Set(Array.from(e.values()));if(e instanceof Object){t.push(e);var r=Object.create(e);for(var i in n.push(r),e){var s=t.findIndex(function(t){return t===e[i]});r[i]=s>-1?n[s]:pt(e[i],t,n)}return r}return e}function pn(e){return pt(e,[],[])}let pr=Object.prototype.toString,pi=Error.prototype.toString,pa=RegExp.prototype.toString,po="undefined"!=typeof Symbol?Symbol.prototype.toString:()=>"",ps=/^Symbol\((.*)\)(.*)$/;function pu(e){if(e!=+e)return"NaN";let t=0===e&&1/e<0;return t?"-0":""+e}function pc(e,t=!1){if(null==e||!0===e||!1===e)return""+e;let n=typeof e;if("number"===n)return pu(e);if("string"===n)return t?`"${e}"`:e;if("function"===n)return"[Function "+(e.name||"anonymous")+"]";if("symbol"===n)return po.call(e).replace(ps,"Symbol($1)");let r=pr.call(e).slice(8,-1);return"Date"===r?isNaN(e.getTime())?""+e:e.toISOString(e):"Error"===r||e instanceof Error?"["+pi.call(e)+"]":"RegExp"===r?pa.call(e):null}function pl(e,t){let n=pc(e,t);return null!==n?n:JSON.stringify(e,function(e,n){let r=pc(this[e],t);return null!==r?r:n},2)}let pf={default:"${path} is invalid",required:"${path} is a required field",oneOf:"${path} must be one of the following values: ${values}",notOneOf:"${path} must not be one of the following values: ${values}",notType({path:e,type:t,value:n,originalValue:r}){let i=null!=r&&r!==n,a=`${e} must be a \`${t}\` type, but the final value was: \`${pl(n,!0)}\``+(i?` (cast from the value \`${pl(r,!0)}\`).`:".");return null===n&&(a+='\n If "null" is intended as an empty value be sure to mark the schema as `.nullable()`'),a},defined:"${path} must be defined"},pd={length:"${path} must be exactly ${length} characters",min:"${path} must be at least ${min} characters",max:"${path} must be at most ${max} characters",matches:'${path} must match the following: "${regex}"',email:"${path} must be a valid email",url:"${path} must be a valid URL",uuid:"${path} must be a valid UUID",trim:"${path} must be a trimmed string",lowercase:"${path} must be a lowercase string",uppercase:"${path} must be a upper case string"},ph={min:"${path} must be greater than or equal to ${min}",max:"${path} must be less than or equal to ${max}",lessThan:"${path} must be less than ${less}",moreThan:"${path} must be greater than ${more}",positive:"${path} must be a positive number",negative:"${path} must be a negative number",integer:"${path} must be an integer"},pp={min:"${path} field must be later than ${min}",max:"${path} field must be at earlier than ${max}"},pb={isValue:"${path} field must be ${value}"},pm={noUnknown:"${path} field has unspecified keys: ${unknown}"},pg={min:"${path} field must have at least ${min} items",max:"${path} field must have less than or equal to ${max} items",length:"${path} must be have ${length} items"};Object.assign(Object.create(null),{mixed:pf,string:pd,number:ph,date:pp,object:pm,array:pg,boolean:pb});var pv=n(18721),py=n.n(pv);let pw=e=>e&&e.__isYupSchema__;class p_{constructor(e,t){if(this.refs=e,this.refs=e,"function"==typeof t){this.fn=t;return}if(!py()(t,"is"))throw TypeError("`is:` is required for `when()` conditions");if(!t.then&&!t.otherwise)throw TypeError("either `then:` or `otherwise:` is required for `when()` conditions");let{is:n,then:r,otherwise:i}=t,a="function"==typeof n?n:(...e)=>e.every(e=>e===n);this.fn=function(...e){let t=e.pop(),n=e.pop(),o=a(...e)?r:i;if(o)return"function"==typeof o?o(n):n.concat(o.resolve(t))}}resolve(e,t){let n=this.refs.map(e=>e.getValue(null==t?void 0:t.value,null==t?void 0:t.parent,null==t?void 0:t.context)),r=this.fn.apply(e,n.concat(e,t));if(void 0===r||r===e)return e;if(!pw(r))throw TypeError("conditions must return a schema object");return r.resolve(t)}}let pE=p_;function pS(e){return null==e?[]:[].concat(e)}function pk(){return(pk=Object.assign||function(e){for(var t=1;tpl(t[n])):"function"==typeof e?e(t):e}static isError(e){return e&&"ValidationError"===e.name}constructor(e,t,n,r){super(),this.name="ValidationError",this.value=t,this.path=n,this.type=r,this.errors=[],this.inner=[],pS(e).forEach(e=>{pT.isError(e)?(this.errors.push(...e.errors),this.inner=this.inner.concat(e.inner.length?e.inner:e)):this.errors.push(e)}),this.message=this.errors.length>1?`${this.errors.length} errors occurred`:this.errors[0],Error.captureStackTrace&&Error.captureStackTrace(this,pT)}}let pM=e=>{let t=!1;return(...n)=>{t||(t=!0,e(...n))}};function pO(e,t){let{endEarly:n,tests:r,args:i,value:a,errors:o,sort:s,path:u}=e,c=pM(t),l=r.length,f=[];if(o=o||[],!l)return o.length?c(new pT(o,a,u)):c(null,a);for(let d=0;d=0||(i[n]=e[n]);return i}function pR(e){function t(t,n){let{value:r,path:i="",label:a,options:o,originalValue:s,sync:u}=t,c=pP(t,["value","path","label","options","originalValue","sync"]),{name:l,test:f,params:d,message:h}=e,{parent:p,context:b}=o;function m(e){return pD.isRef(e)?e.getValue(r,p,b):e}function g(e={}){let t=pL()(pN({value:r,originalValue:s,label:a,path:e.path||i},d,e.params),m),n=new pT(pT.formatError(e.message||h,t),r,t.path,e.type||l);return n.params=t,n}let v=pN({path:i,parent:p,type:l,createError:g,resolve:m,options:o,originalValue:s},c);if(!u){try{Promise.resolve(f.call(v,r,v)).then(e=>{pT.isError(e)?n(e):e?n(null,e):n(g())})}catch(y){n(y)}return}let w;try{var _;if(w=f.call(v,r,v),"function"==typeof(null==(_=w)?void 0:_.then))throw Error(`Validation test of type: "${v.type}" returned a Promise during a synchronous validate. This test will finish after the validate call has returned`)}catch(E){n(E);return}pT.isError(w)?n(w):w?n(null,w):n(g())}return t.OPTIONS=e,t}pD.prototype.__isYupRef=!0;let pj=e=>e.substr(0,e.length-1).substr(1);function pF(e,t,n,r=n){let i,a,o;return t?((0,pC.forEach)(t,(s,u,c)=>{let l=u?pj(s):s;if((e=e.resolve({context:r,parent:i,value:n})).innerType){let f=c?parseInt(l,10):0;if(n&&f>=n.length)throw Error(`Yup.reach cannot resolve an array item at index: ${s}, in the path: ${t}. because there is no value at that index. `);i=n,n=n&&n[f],e=e.innerType}if(!c){if(!e.fields||!e.fields[l])throw Error(`The schema does not contain the path: ${t}. (failed at: ${o} which is a type: "${e._type}")`);i=n,n=n&&n[l],e=e.fields[l]}a=l,o=u?"["+s+"]":"."+s}),{schema:e,parent:i,parentPath:a}):{parent:i,parentPath:t,schema:e}}class pY{constructor(){this.list=new Set,this.refs=new Map}get size(){return this.list.size+this.refs.size}describe(){let e=[];for(let t of this.list)e.push(t);for(let[,n]of this.refs)e.push(n.describe());return e}toArray(){return Array.from(this.list).concat(Array.from(this.refs.values()))}add(e){pD.isRef(e)?this.refs.set(e.key,e):this.list.add(e)}delete(e){pD.isRef(e)?this.refs.delete(e.key):this.list.delete(e)}has(e,t){if(this.list.has(e))return!0;let n,r=this.refs.values();for(;!(n=r.next()).done;)if(t(n.value)===e)return!0;return!1}clone(){let e=new pY;return e.list=new Set(this.list),e.refs=new Map(this.refs),e}merge(e,t){let n=this.clone();return e.list.forEach(e=>n.add(e)),e.refs.forEach(e=>n.add(e)),t.list.forEach(e=>n.delete(e)),t.refs.forEach(e=>n.delete(e)),n}}function pB(){return(pB=Object.assign||function(e){for(var t=1;t{this.typeError(pf.notType)}),this.type=(null==e?void 0:e.type)||"mixed",this.spec=pB({strip:!1,strict:!1,abortEarly:!0,recursive:!0,nullable:!1,presence:"optional"},null==e?void 0:e.spec)}get _type(){return this.type}_typeCheck(e){return!0}clone(e){if(this._mutate)return e&&Object.assign(this.spec,e),this;let t=Object.create(Object.getPrototypeOf(this));return t.type=this.type,t._typeError=this._typeError,t._whitelistError=this._whitelistError,t._blacklistError=this._blacklistError,t._whitelist=this._whitelist.clone(),t._blacklist=this._blacklist.clone(),t.exclusiveTests=pB({},this.exclusiveTests),t.deps=[...this.deps],t.conditions=[...this.conditions],t.tests=[...this.tests],t.transforms=[...this.transforms],t.spec=pn(pB({},this.spec,e)),t}label(e){var t=this.clone();return t.spec.label=e,t}meta(...e){if(0===e.length)return this.spec.meta;let t=this.clone();return t.spec.meta=Object.assign(t.spec.meta||{},e[0]),t}withMutation(e){let t=this._mutate;this._mutate=!0;let n=e(this);return this._mutate=t,n}concat(e){if(!e||e===this)return this;if(e.type!==this.type&&"mixed"!==this.type)throw TypeError(`You cannot \`concat()\` schema's of different types: ${this.type} and ${e.type}`);let t=this,n=e.clone(),r=pB({},t.spec,n.spec);return n.spec=r,n._typeError||(n._typeError=t._typeError),n._whitelistError||(n._whitelistError=t._whitelistError),n._blacklistError||(n._blacklistError=t._blacklistError),n._whitelist=t._whitelist.merge(e._whitelist,e._blacklist),n._blacklist=t._blacklist.merge(e._blacklist,e._whitelist),n.tests=t.tests,n.exclusiveTests=t.exclusiveTests,n.withMutation(t=>{e.tests.forEach(e=>{t.test(e.OPTIONS)})}),n}isType(e){return!!this.spec.nullable&&null===e||this._typeCheck(e)}resolve(e){let t=this;if(t.conditions.length){let n=t.conditions;(t=t.clone()).conditions=[],t=(t=n.reduce((t,n)=>n.resolve(t,e),t)).resolve(e)}return t}cast(e,t={}){let n=this.resolve(pB({value:e},t)),r=n._cast(e,t);if(void 0!==e&&!1!==t.assert&&!0!==n.isType(r)){let i=pl(e),a=pl(r);throw TypeError(`The value of ${t.path||"field"} could not be cast to a value that satisfies the schema type: "${n._type}". attempted value: ${i} -`+(a!==i?`result of cast: ${a}`:""))}return r}_cast(e,t){let n=void 0===e?e:this.transforms.reduce((t,n)=>n.call(this,t,e,this),e);return void 0===n&&(n=this.getDefault()),n}_validate(e,t={},n){let{sync:r,path:i,from:a=[],originalValue:o=e,strict:s=this.spec.strict,abortEarly:u=this.spec.abortEarly}=t,c=e;s||(c=this._cast(c,pB({assert:!1},t)));let l={value:c,path:i,options:t,originalValue:o,schema:this,label:this.spec.label,sync:r,from:a},f=[];this._typeError&&f.push(this._typeError),this._whitelistError&&f.push(this._whitelistError),this._blacklistError&&f.push(this._blacklistError),pO({args:l,value:c,path:i,sync:r,tests:f,endEarly:u},e=>{if(e)return void n(e,c);pO({tests:this.tests,args:l,path:i,sync:r,value:c,endEarly:u},n)})}validate(e,t,n){let r=this.resolve(pB({},t,{value:e}));return"function"==typeof n?r._validate(e,t,n):new Promise((n,i)=>r._validate(e,t,(e,t)=>{e?i(e):n(t)}))}validateSync(e,t){let n;return this.resolve(pB({},t,{value:e}))._validate(e,pB({},t,{sync:!0}),(e,t)=>{if(e)throw e;n=t}),n}isValid(e,t){return this.validate(e,t).then(()=>!0,e=>{if(pT.isError(e))return!1;throw e})}isValidSync(e,t){try{return this.validateSync(e,t),!0}catch(n){if(pT.isError(n))return!1;throw n}}_getDefault(){let e=this.spec.default;return null==e?e:"function"==typeof e?e.call(this):pn(e)}getDefault(e){return this.resolve(e||{})._getDefault()}default(e){return 0===arguments.length?this._getDefault():this.clone({default:e})}strict(e=!0){var t=this.clone();return t.spec.strict=e,t}_isPresent(e){return null!=e}defined(e=pf.defined){return this.test({message:e,name:"defined",exclusive:!0,test:e=>void 0!==e})}required(e=pf.required){return this.clone({presence:"required"}).withMutation(t=>t.test({message:e,name:"required",exclusive:!0,test(e){return this.schema._isPresent(e)}}))}notRequired(){var e=this.clone({presence:"optional"});return e.tests=e.tests.filter(e=>"required"!==e.OPTIONS.name),e}nullable(e=!0){return this.clone({nullable:!1!==e})}transform(e){var t=this.clone();return t.transforms.push(e),t}test(...e){let t;if(void 0===(t=1===e.length?"function"==typeof e[0]?{test:e[0]}:e[0]:2===e.length?{name:e[0],test:e[1]}:{name:e[0],message:e[1],test:e[2]}).message&&(t.message=pf.default),"function"!=typeof t.test)throw TypeError("`test` is a required parameters");let n=this.clone(),r=pR(t),i=t.exclusive||t.name&&!0===n.exclusiveTests[t.name];if(t.exclusive&&!t.name)throw TypeError("Exclusive tests must provide a unique `name` identifying the test");return t.name&&(n.exclusiveTests[t.name]=!!t.exclusive),n.tests=n.tests.filter(e=>e.OPTIONS.name!==t.name||!i&&e.OPTIONS.test!==r.OPTIONS.test),n.tests.push(r),n}when(e,t){Array.isArray(e)||"string"==typeof e||(t=e,e=".");let n=this.clone(),r=pS(e).map(e=>new pD(e));return r.forEach(e=>{e.isSibling&&n.deps.push(e.key)}),n.conditions.push(new pE(r,t)),n}typeError(e){var t=this.clone();return t._typeError=pR({message:e,name:"typeError",test(e){return!!(void 0===e||this.schema.isType(e))||this.createError({params:{type:this.schema._type}})}}),t}oneOf(e,t=pf.oneOf){var n=this.clone();return e.forEach(e=>{n._whitelist.add(e),n._blacklist.delete(e)}),n._whitelistError=pR({message:t,name:"oneOf",test(e){if(void 0===e)return!0;let t=this.schema._whitelist;return!!t.has(e,this.resolve)||this.createError({params:{values:t.toArray().join(", ")}})}}),n}notOneOf(e,t=pf.notOneOf){var n=this.clone();return e.forEach(e=>{n._blacklist.add(e),n._whitelist.delete(e)}),n._blacklistError=pR({message:t,name:"notOneOf",test(e){let t=this.schema._blacklist;return!t.has(e,this.resolve)||this.createError({params:{values:t.toArray().join(", ")}})}}),n}strip(e=!0){let t=this.clone();return t.spec.strip=e,t}describe(){let e=this.clone(),{label:t,meta:n}=e.spec,r={meta:n,label:t,type:e.type,oneOf:e._whitelist.describe(),notOneOf:e._blacklist.describe(),tests:e.tests.map(e=>({name:e.OPTIONS.name,params:e.OPTIONS.params})).filter((e,t,n)=>n.findIndex(t=>t.name===e.name)===t)};return r}}for(let pH of(pU.prototype.__isYupSchema__=!0,["validate","validateSync"]))pU.prototype[`${pH}At`]=function(e,t,n={}){let{parent:r,parentPath:i,schema:a}=pF(this,e,t,n.context);return a[pH](r&&r[i],pB({},n,{parent:r,path:e}))};for(let p$ of["equals","is"])pU.prototype[p$]=pU.prototype.oneOf;for(let pz of["not","nope"])pU.prototype[pz]=pU.prototype.notOneOf;pU.prototype.optional=pU.prototype.notRequired;let pG=pU;function pW(){return new pG}pW.prototype=pG.prototype;let pK=e=>null==e;function pV(){return new pq}class pq extends pU{constructor(){super({type:"boolean"}),this.withMutation(()=>{this.transform(function(e){if(!this.isType(e)){if(/^(true|1)$/i.test(String(e)))return!0;if(/^(false|0)$/i.test(String(e)))return!1}return e})})}_typeCheck(e){return e instanceof Boolean&&(e=e.valueOf()),"boolean"==typeof e}isTrue(e=pb.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"true"},test:e=>pK(e)||!0===e})}isFalse(e=pb.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"false"},test:e=>pK(e)||!1===e})}}pV.prototype=pq.prototype;let pZ=/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i,pX=/^((https?|ftp):)?\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i,pJ=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i,pQ=e=>pK(e)||e===e.trim(),p1=({}).toString();function p0(){return new p2}class p2 extends pU{constructor(){super({type:"string"}),this.withMutation(()=>{this.transform(function(e){if(this.isType(e)||Array.isArray(e))return e;let t=null!=e&&e.toString?e.toString():e;return t===p1?e:t})})}_typeCheck(e){return e instanceof String&&(e=e.valueOf()),"string"==typeof e}_isPresent(e){return super._isPresent(e)&&!!e.length}length(e,t=pd.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pK(t)||t.length===this.resolve(e)}})}min(e,t=pd.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pK(t)||t.length>=this.resolve(e)}})}max(e,t=pd.max){return this.test({name:"max",exclusive:!0,message:t,params:{max:e},test(t){return pK(t)||t.length<=this.resolve(e)}})}matches(e,t){let n=!1,r,i;return t&&("object"==typeof t?{excludeEmptyString:n=!1,message:r,name:i}=t:r=t),this.test({name:i||"matches",message:r||pd.matches,params:{regex:e},test:t=>pK(t)||""===t&&n||-1!==t.search(e)})}email(e=pd.email){return this.matches(pZ,{name:"email",message:e,excludeEmptyString:!0})}url(e=pd.url){return this.matches(pX,{name:"url",message:e,excludeEmptyString:!0})}uuid(e=pd.uuid){return this.matches(pJ,{name:"uuid",message:e,excludeEmptyString:!1})}ensure(){return this.default("").transform(e=>null===e?"":e)}trim(e=pd.trim){return this.transform(e=>null!=e?e.trim():e).test({message:e,name:"trim",test:pQ})}lowercase(e=pd.lowercase){return this.transform(e=>pK(e)?e:e.toLowerCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pK(e)||e===e.toLowerCase()})}uppercase(e=pd.uppercase){return this.transform(e=>pK(e)?e:e.toUpperCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pK(e)||e===e.toUpperCase()})}}p0.prototype=p2.prototype;let p3=e=>e!=+e;function p4(){return new p6}class p6 extends pU{constructor(){super({type:"number"}),this.withMutation(()=>{this.transform(function(e){let t=e;if("string"==typeof t){if(""===(t=t.replace(/\s/g,"")))return NaN;t=+t}return this.isType(t)?t:parseFloat(t)})})}_typeCheck(e){return e instanceof Number&&(e=e.valueOf()),"number"==typeof e&&!p3(e)}min(e,t=ph.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pK(t)||t>=this.resolve(e)}})}max(e,t=ph.max){return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pK(t)||t<=this.resolve(e)}})}lessThan(e,t=ph.lessThan){return this.test({message:t,name:"max",exclusive:!0,params:{less:e},test(t){return pK(t)||tthis.resolve(e)}})}positive(e=ph.positive){return this.moreThan(0,e)}negative(e=ph.negative){return this.lessThan(0,e)}integer(e=ph.integer){return this.test({name:"integer",message:e,test:e=>pK(e)||Number.isInteger(e)})}truncate(){return this.transform(e=>pK(e)?e:0|e)}round(e){var t,n=["ceil","floor","round","trunc"];if("trunc"===(e=(null==(t=e)?void 0:t.toLowerCase())||"round"))return this.truncate();if(-1===n.indexOf(e.toLowerCase()))throw TypeError("Only valid options for round() are: "+n.join(", "));return this.transform(t=>pK(t)?t:Math[e](t))}}p4.prototype=p6.prototype;var p5=/^(\d{4}|[+\-]\d{6})(?:-?(\d{2})(?:-?(\d{2}))?)?(?:[ T]?(\d{2}):?(\d{2})(?::?(\d{2})(?:[,\.](\d{1,}))?)?(?:(Z)|([+\-])(\d{2})(?::?(\d{2}))?)?)?$/;function p8(e){var t,n,r=[1,4,5,6,7,10,11],i=0;if(n=p5.exec(e)){for(var a,o=0;a=r[o];++o)n[a]=+n[a]||0;n[2]=(+n[2]||1)-1,n[3]=+n[3]||1,n[7]=n[7]?String(n[7]).substr(0,3):0,(void 0===n[8]||""===n[8])&&(void 0===n[9]||""===n[9])?t=+new Date(n[1],n[2],n[3],n[4],n[5],n[6],n[7]):("Z"!==n[8]&&void 0!==n[9]&&(i=60*n[10]+n[11],"+"===n[9]&&(i=0-i)),t=Date.UTC(n[1],n[2],n[3],n[4],n[5]+i,n[6],n[7]))}else t=Date.parse?Date.parse(e):NaN;return t}let p9=new Date(""),p7=e=>"[object Date]"===Object.prototype.toString.call(e);function be(){return new bt}class bt extends pU{constructor(){super({type:"date"}),this.withMutation(()=>{this.transform(function(e){return this.isType(e)?e:(e=p8(e),isNaN(e)?p9:new Date(e))})})}_typeCheck(e){return p7(e)&&!isNaN(e.getTime())}prepareParam(e,t){let n;if(pD.isRef(e))n=e;else{let r=this.cast(e);if(!this._typeCheck(r))throw TypeError(`\`${t}\` must be a Date or a value that can be \`cast()\` to a Date`);n=r}return n}min(e,t=pp.min){let n=this.prepareParam(e,"min");return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(e){return pK(e)||e>=this.resolve(n)}})}max(e,t=pp.max){var n=this.prepareParam(e,"max");return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(e){return pK(e)||e<=this.resolve(n)}})}}bt.INVALID_DATE=p9,be.prototype=bt.prototype,be.INVALID_DATE=p9;var bn=n(11865),br=n.n(bn),bi=n(68929),ba=n.n(bi),bo=n(67523),bs=n.n(bo),bu=n(94633),bc=n.n(bu);function bl(e,t=[]){let n=[],r=[];function i(e,i){var a=(0,pC.split)(e)[0];~r.indexOf(a)||r.push(a),~t.indexOf(`${i}-${a}`)||n.push([i,a])}for(let a in e)if(py()(e,a)){let o=e[a];~r.indexOf(a)||r.push(a),pD.isRef(o)&&o.isSibling?i(o.path,a):pw(o)&&"deps"in o&&o.deps.forEach(e=>i(e,a))}return bc().array(r,n).reverse()}function bf(e,t){let n=1/0;return e.some((e,r)=>{var i;if((null==(i=t.path)?void 0:i.indexOf(e))!==-1)return n=r,!0}),n}function bd(e){return(t,n)=>bf(e,t)-bf(e,n)}function bh(){return(bh=Object.assign||function(e){for(var t=1;t"[object Object]"===Object.prototype.toString.call(e);function bb(e,t){let n=Object.keys(e.fields);return Object.keys(t).filter(e=>-1===n.indexOf(e))}let bm=bd([]);class bg extends pU{constructor(e){super({type:"object"}),this.fields=Object.create(null),this._sortErrors=bm,this._nodes=[],this._excludedEdges=[],this.withMutation(()=>{this.transform(function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(t){e=null}return this.isType(e)?e:null}),e&&this.shape(e)})}_typeCheck(e){return bp(e)||"function"==typeof e}_cast(e,t={}){var n;let r=super._cast(e,t);if(void 0===r)return this.getDefault();if(!this._typeCheck(r))return r;let i=this.fields,a=null!=(n=t.stripUnknown)?n:this.spec.noUnknown,o=this._nodes.concat(Object.keys(r).filter(e=>-1===this._nodes.indexOf(e))),s={},u=bh({},t,{parent:s,__validating:t.__validating||!1}),c=!1;for(let l of o){let f=i[l],d=py()(r,l);if(f){let h,p=r[l];u.path=(t.path?`${t.path}.`:"")+l;let b="spec"in(f=f.resolve({value:p,context:t.context,parent:s}))?f.spec:void 0,m=null==b?void 0:b.strict;if(null==b?void 0:b.strip){c=c||l in r;continue}void 0!==(h=t.__validating&&m?r[l]:f.cast(r[l],u))&&(s[l]=h)}else d&&!a&&(s[l]=r[l]);s[l]!==r[l]&&(c=!0)}return c?s:r}_validate(e,t={},n){let r=[],{sync:i,from:a=[],originalValue:o=e,abortEarly:s=this.spec.abortEarly,recursive:u=this.spec.recursive}=t;a=[{schema:this,value:o},...a],t.__validating=!0,t.originalValue=o,t.from=a,super._validate(e,t,(e,c)=>{if(e){if(!pT.isError(e)||s)return void n(e,c);r.push(e)}if(!u||!bp(c)){n(r[0]||null,c);return}o=o||c;let l=this._nodes.map(e=>(n,r)=>{let i=-1===e.indexOf(".")?(t.path?`${t.path}.`:"")+e:`${t.path||""}["${e}"]`,s=this.fields[e];if(s&&"validate"in s){s.validate(c[e],bh({},t,{path:i,from:a,strict:!0,parent:c,originalValue:o[e]}),r);return}r(null)});pO({sync:i,tests:l,value:c,errors:r,endEarly:s,sort:this._sortErrors,path:t.path},n)})}clone(e){let t=super.clone(e);return t.fields=bh({},this.fields),t._nodes=this._nodes,t._excludedEdges=this._excludedEdges,t._sortErrors=this._sortErrors,t}concat(e){let t=super.concat(e),n=t.fields;for(let[r,i]of Object.entries(this.fields)){let a=n[r];void 0===a?n[r]=i:a instanceof pU&&i instanceof pU&&(n[r]=i.concat(a))}return t.withMutation(()=>t.shape(n))}getDefaultFromShape(){let e={};return this._nodes.forEach(t=>{let n=this.fields[t];e[t]="default"in n?n.getDefault():void 0}),e}_getDefault(){return"default"in this.spec?super._getDefault():this._nodes.length?this.getDefaultFromShape():void 0}shape(e,t=[]){let n=this.clone(),r=Object.assign(n.fields,e);if(n.fields=r,n._sortErrors=bd(Object.keys(r)),t.length){Array.isArray(t[0])||(t=[t]);let i=t.map(([e,t])=>`${e}-${t}`);n._excludedEdges=n._excludedEdges.concat(i)}return n._nodes=bl(r,n._excludedEdges),n}pick(e){let t={};for(let n of e)this.fields[n]&&(t[n]=this.fields[n]);return this.clone().withMutation(e=>(e.fields={},e.shape(t)))}omit(e){let t=this.clone(),n=t.fields;for(let r of(t.fields={},e))delete n[r];return t.withMutation(()=>t.shape(n))}from(e,t,n){let r=(0,pC.getter)(e,!0);return this.transform(i=>{if(null==i)return i;let a=i;return py()(i,e)&&(a=bh({},i),n||delete a[e],a[t]=r(i)),a})}noUnknown(e=!0,t=pm.noUnknown){"string"==typeof e&&(t=e,e=!0);let n=this.test({name:"noUnknown",exclusive:!0,message:t,test(t){if(null==t)return!0;let n=bb(this.schema,t);return!e||0===n.length||this.createError({params:{unknown:n.join(", ")}})}});return n.spec.noUnknown=e,n}unknown(e=!0,t=pm.noUnknown){return this.noUnknown(!e,t)}transformKeys(e){return this.transform(t=>t&&bs()(t,(t,n)=>e(n)))}camelCase(){return this.transformKeys(ba())}snakeCase(){return this.transformKeys(br())}constantCase(){return this.transformKeys(e=>br()(e).toUpperCase())}describe(){let e=super.describe();return e.fields=pL()(this.fields,e=>e.describe()),e}}function bv(e){return new bg(e)}function by(){return(by=Object.assign||function(e){for(var t=1;t{this.transform(function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(t){e=null}return this.isType(e)?e:null})})}_typeCheck(e){return Array.isArray(e)}get _subType(){return this.innerType}_cast(e,t){let n=super._cast(e,t);if(!this._typeCheck(n)||!this.innerType)return n;let r=!1,i=n.map((e,n)=>{let i=this.innerType.cast(e,by({},t,{path:`${t.path||""}[${n}]`}));return i!==e&&(r=!0),i});return r?i:n}_validate(e,t={},n){var r,i;let a=[],o=t.sync,s=t.path,u=this.innerType,c=null!=(r=t.abortEarly)?r:this.spec.abortEarly,l=null!=(i=t.recursive)?i:this.spec.recursive,f=null!=t.originalValue?t.originalValue:e;super._validate(e,t,(e,r)=>{if(e){if(!pT.isError(e)||c)return void n(e,r);a.push(e)}if(!l||!u||!this._typeCheck(r)){n(a[0]||null,r);return}f=f||r;let i=Array(r.length);for(let d=0;du.validate(h,b,t)}pO({sync:o,path:s,value:r,errors:a,endEarly:c,tests:i},n)})}clone(e){let t=super.clone(e);return t.innerType=this.innerType,t}concat(e){let t=super.concat(e);return t.innerType=this.innerType,e.innerType&&(t.innerType=t.innerType?t.innerType.concat(e.innerType):e.innerType),t}of(e){let t=this.clone();if(!pw(e))throw TypeError("`array.of()` sub-schema must be a valid yup schema not: "+pl(e));return t.innerType=e,t}length(e,t=pg.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pK(t)||t.length===this.resolve(e)}})}min(e,t){return t=t||pg.min,this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pK(t)||t.length>=this.resolve(e)}})}max(e,t){return t=t||pg.max,this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pK(t)||t.length<=this.resolve(e)}})}ensure(){return this.default(()=>[]).transform((e,t)=>this._typeCheck(e)?e:null==t?[]:[].concat(t))}compact(e){let t=e?(t,n,r)=>!e(t,n,r):e=>!!e;return this.transform(e=>null!=e?e.filter(t):e)}describe(){let e=super.describe();return this.innerType&&(e.innerType=this.innerType.describe()),e}nullable(e=!0){return super.nullable(e)}defined(){return super.defined()}required(e){return super.required(e)}}bw.prototype=b_.prototype;var bE=bv().shape({name:p0().required("Required"),url:p0().required("Required")}),bS=function(e){var t=e.initialValues,n=e.onSubmit,r=e.submitButtonText,i=e.nameDisabled,a=void 0!==i&&i;return l.createElement(hT,{initialValues:t,validationSchema:bE,onSubmit:n},function(e){var t=e.isSubmitting;return l.createElement(l.Fragment,null,l.createElement(hR,{"data-testid":"bridge-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(hP,{component:hX,id:"name",name:"name",label:"Name",disabled:a,required:!0,fullWidth:!0,FormHelperTextProps:{"data-testid":"name-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(hP,{component:hX,id:"url",name:"url",label:"Bridge URL",placeholder:"https://",required:!0,fullWidth:!0,FormHelperTextProps:{"data-testid":"url-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:7},l.createElement(hP,{component:hX,id:"minimumContractPayment",name:"minimumContractPayment",label:"Minimum Contract Payment",placeholder:"0",fullWidth:!0,inputProps:{min:0},FormHelperTextProps:{"data-testid":"minimumContractPayment-helper-text"}})),l.createElement(d.Z,{item:!0,xs:7},l.createElement(hP,{component:hX,id:"confirmations",name:"confirmations",label:"Confirmations",placeholder:"0",type:"number",fullWidth:!0,inputProps:{min:0},FormHelperTextProps:{"data-testid":"confirmations-helper-text"}})))),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(ok.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},r)))))})},bk=function(e){var t=e.bridge,n=e.onSubmit,r={name:t.name,url:t.url,minimumContractPayment:t.minimumContractPayment,confirmations:t.confirmations};return l.createElement(ig,null,l.createElement(d.Z,{container:!0,spacing:40},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"Edit Bridge",action:l.createElement(aA.Z,{component:tz,href:"/bridges/".concat(t.id)},"Cancel")}),l.createElement(aW.Z,null,l.createElement(bS,{nameDisabled:!0,initialValues:r,onSubmit:n,submitButtonText:"Save Bridge"}))))))};function bx(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&void 0!==arguments[0]&&arguments[0],t=e?function(){return l.createElement(x.default,{variant:"body1"},"Loading...")}:function(){return null};return{isLoading:e,LoadingPlaceholder:t}},mc=n(76023);function ml(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function mB(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n=4?[e[0],e[1],e[2],e[3],"".concat(e[0],".").concat(e[1]),"".concat(e[0],".").concat(e[2]),"".concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[0]),"".concat(e[1],".").concat(e[2]),"".concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[1]),"".concat(e[2],".").concat(e[3]),"".concat(e[3],".").concat(e[0]),"".concat(e[3],".").concat(e[1]),"".concat(e[3],".").concat(e[2]),"".concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[1],".").concat(e[3]),"".concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[2],".").concat(e[3]),"".concat(e[0],".").concat(e[3],".").concat(e[1]),"".concat(e[0],".").concat(e[3],".").concat(e[2]),"".concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[1],".").concat(e[2],".").concat(e[3]),"".concat(e[1],".").concat(e[3],".").concat(e[0]),"".concat(e[1],".").concat(e[3],".").concat(e[2]),"".concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[0],".").concat(e[3]),"".concat(e[2],".").concat(e[1],".").concat(e[0]),"".concat(e[2],".").concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[3],".").concat(e[0]),"".concat(e[2],".").concat(e[3],".").concat(e[1]),"".concat(e[3],".").concat(e[0],".").concat(e[1]),"".concat(e[3],".").concat(e[0],".").concat(e[2]),"".concat(e[3],".").concat(e[1],".").concat(e[0]),"".concat(e[3],".").concat(e[1],".").concat(e[2]),"".concat(e[3],".").concat(e[2],".").concat(e[0]),"".concat(e[3],".").concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[1],".").concat(e[2],".").concat(e[3]),"".concat(e[0],".").concat(e[1],".").concat(e[3],".").concat(e[2]),"".concat(e[0],".").concat(e[2],".").concat(e[1],".").concat(e[3]),"".concat(e[0],".").concat(e[2],".").concat(e[3],".").concat(e[1]),"".concat(e[0],".").concat(e[3],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[3],".").concat(e[2],".").concat(e[1]),"".concat(e[1],".").concat(e[0],".").concat(e[2],".").concat(e[3]),"".concat(e[1],".").concat(e[0],".").concat(e[3],".").concat(e[2]),"".concat(e[1],".").concat(e[2],".").concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[2],".").concat(e[3],".").concat(e[0]),"".concat(e[1],".").concat(e[3],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[3],".").concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[0],".").concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[0],".").concat(e[3],".").concat(e[1]),"".concat(e[2],".").concat(e[1],".").concat(e[0],".").concat(e[3]),"".concat(e[2],".").concat(e[1],".").concat(e[3],".").concat(e[0]),"".concat(e[2],".").concat(e[3],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[3],".").concat(e[1],".").concat(e[0]),"".concat(e[3],".").concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[3],".").concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[3],".").concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[3],".").concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[3],".").concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[3],".").concat(e[2],".").concat(e[1],".").concat(e[0])]:void 0}var mZ={};function mX(e){if(0===e.length||1===e.length)return e;var t=e.join(".");return mZ[t]||(mZ[t]=mq(e)),mZ[t]}function mJ(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0;return mX(e.filter(function(e){return"token"!==e})).reduce(function(e,t){return mK({},e,n[t])},t)}function mQ(e){return e.join(" ")}function m1(e,t){var n=0;return function(r){return n+=1,r.map(function(r,i){return m0({node:r,stylesheet:e,useInlineStyles:t,key:"code-segment-".concat(n,"-").concat(i)})})}}function m0(e){var t=e.node,n=e.stylesheet,r=e.style,i=void 0===r?{}:r,a=e.useInlineStyles,o=e.key,s=t.properties,u=t.type,c=t.tagName,f=t.value;if("text"===u)return f;if(c){var d,h=m1(n,a);if(a){var p=Object.keys(n).reduce(function(e,t){return t.split(".").forEach(function(t){e.includes(t)||e.push(t)}),e},[]),b=s.className&&s.className.includes("token")?["token"]:[],m=s.className&&b.concat(s.className.filter(function(e){return!p.includes(e)}));d=mK({},s,{className:mQ(m)||void 0,style:mJ(s.className,Object.assign({},s.style,i),n)})}else d=mK({},s,{className:mQ(s.className)});var g=h(t.children);return l.createElement(c,(0,mV.Z)({key:o},d),g)}}let m2=function(e,t){return -1!==e.listLanguages().indexOf(t)};var m3=/\n/g;function m4(e){return e.match(m3)}function m6(e){var t=e.lines,n=e.startingLineNumber,r=e.style;return t.map(function(e,t){var i=t+n;return l.createElement("span",{key:"line-".concat(t),className:"react-syntax-highlighter-line-number",style:"function"==typeof r?r(i):r},"".concat(i,"\n"))})}function m5(e){var t=e.codeString,n=e.codeStyle,r=e.containerStyle,i=void 0===r?{float:"left",paddingRight:"10px"}:r,a=e.numberStyle,o=void 0===a?{}:a,s=e.startingLineNumber;return l.createElement("code",{style:Object.assign({},n,i)},m6({lines:t.replace(/\n$/,"").split("\n"),style:o,startingLineNumber:s}))}function m8(e){return"".concat(e.toString().length,".25em")}function m9(e,t){return{type:"element",tagName:"span",properties:{key:"line-number--".concat(e),className:["comment","linenumber","react-syntax-highlighter-line-number"],style:t},children:[{type:"text",value:e}]}}function m7(e,t,n){var r,i={display:"inline-block",minWidth:m8(n),paddingRight:"1em",textAlign:"right",userSelect:"none"};return mK({},i,"function"==typeof e?e(t):e)}function ge(e){var t=e.children,n=e.lineNumber,r=e.lineNumberStyle,i=e.largestLineNumber,a=e.showInlineLineNumbers,o=e.lineProps,s=void 0===o?{}:o,u=e.className,c=void 0===u?[]:u,l=e.showLineNumbers,f=e.wrapLongLines,d="function"==typeof s?s(n):s;if(d.className=c,n&&a){var h=m7(r,n,i);t.unshift(m9(n,h))}return f&l&&(d.style=mK({},d.style,{display:"flex"})),{type:"element",tagName:"span",properties:d,children:t}}function gt(e){for(var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],r=0;r2&&void 0!==arguments[2]?arguments[2]:[];return ge({children:e,lineNumber:t,lineNumberStyle:s,largestLineNumber:o,showInlineLineNumbers:i,lineProps:n,className:a,showLineNumbers:r,wrapLongLines:u})}function b(e,t){if(r&&t&&i){var n=m7(s,t,o);e.unshift(m9(t,n))}return e}function m(e,n){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[];return t||r.length>0?p(e,n,r):b(e,n)}for(var g=function(){var e=l[h],t=e.children[0].value;if(m4(t)){var n=t.split("\n");n.forEach(function(t,i){var o=r&&f.length+a,s={type:"text",value:"".concat(t,"\n")};if(0===i){var u=l.slice(d+1,h).concat(ge({children:[s],className:e.properties.className})),c=m(u,o);f.push(c)}else if(i===n.length-1){if(l[h+1]&&l[h+1].children&&l[h+1].children[0]){var p={type:"text",value:"".concat(t)},b=ge({children:[p],className:e.properties.className});l.splice(h+1,0,b)}else{var g=[s],v=m(g,o,e.properties.className);f.push(v)}}else{var y=[s],w=m(y,o,e.properties.className);f.push(w)}}),d=h}h++};h code[class*="language-"]':{background:"#f5f2f0",padding:".1em",borderRadius:".3em",whiteSpace:"normal"},comment:{color:"slategray"},prolog:{color:"slategray"},doctype:{color:"slategray"},cdata:{color:"slategray"},punctuation:{color:"#999"},namespace:{Opacity:".7"},property:{color:"#905"},tag:{color:"#905"},boolean:{color:"#905"},number:{color:"#905"},constant:{color:"#905"},symbol:{color:"#905"},deleted:{color:"#905"},selector:{color:"#690"},"attr-name":{color:"#690"},string:{color:"#690"},char:{color:"#690"},builtin:{color:"#690"},inserted:{color:"#690"},operator:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},entity:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)",cursor:"help"},url:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},".language-css .token.string":{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},".style .token.string":{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},atrule:{color:"#07a"},"attr-value":{color:"#07a"},keyword:{color:"#07a"},function:{color:"#DD4A68"},"class-name":{color:"#DD4A68"},regex:{color:"#e90"},important:{color:"#e90",fontWeight:"bold"},variable:{color:"#e90"},bold:{fontWeight:"bold"},italic:{fontStyle:"italic"}};var gu=n(98695),gc=n.n(gu);let gl=["abap","abnf","actionscript","ada","agda","al","antlr4","apacheconf","apl","applescript","aql","arduino","arff","asciidoc","asm6502","aspnet","autohotkey","autoit","bash","basic","batch","bbcode","birb","bison","bnf","brainfuck","brightscript","bro","bsl","c","cil","clike","clojure","cmake","coffeescript","concurnas","cpp","crystal","csharp","csp","css-extras","css","cypher","d","dart","dax","dhall","diff","django","dns-zone-file","docker","ebnf","editorconfig","eiffel","ejs","elixir","elm","erb","erlang","etlua","excel-formula","factor","firestore-security-rules","flow","fortran","fsharp","ftl","gcode","gdscript","gedcom","gherkin","git","glsl","gml","go","graphql","groovy","haml","handlebars","haskell","haxe","hcl","hlsl","hpkp","hsts","http","ichigojam","icon","iecst","ignore","inform7","ini","io","j","java","javadoc","javadoclike","javascript","javastacktrace","jolie","jq","js-extras","js-templates","jsdoc","json","json5","jsonp","jsstacktrace","jsx","julia","keyman","kotlin","latex","latte","less","lilypond","liquid","lisp","livescript","llvm","lolcode","lua","makefile","markdown","markup-templating","markup","matlab","mel","mizar","mongodb","monkey","moonscript","n1ql","n4js","nand2tetris-hdl","naniscript","nasm","neon","nginx","nim","nix","nsis","objectivec","ocaml","opencl","oz","parigp","parser","pascal","pascaligo","pcaxis","peoplecode","perl","php-extras","php","phpdoc","plsql","powerquery","powershell","processing","prolog","properties","protobuf","pug","puppet","pure","purebasic","purescript","python","q","qml","qore","r","racket","reason","regex","renpy","rest","rip","roboconf","robotframework","ruby","rust","sas","sass","scala","scheme","scss","shell-session","smali","smalltalk","smarty","sml","solidity","solution-file","soy","sparql","splunk-spl","sqf","sql","stan","stylus","swift","t4-cs","t4-templating","t4-vb","tap","tcl","textile","toml","tsx","tt2","turtle","twig","typescript","typoscript","unrealscript","vala","vbnet","velocity","verilog","vhdl","vim","visual-basic","warpscript","wasm","wiki","xeora","xml-doc","xojo","xquery","yaml","yang","zig"];var gf=go(gc(),gs);gf.supportedLanguages=gl;let gd=gf;var gh=n(64566);function gp(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function gb(){var e=gp(["\n query FetchConfigV2 {\n configv2 {\n user\n effective\n }\n }\n"]);return gb=function(){return e},e}var gm=n0(gb()),gg=function(e){var t=e.children;return l.createElement(ir.Z,null,l.createElement(r7.default,{component:"th",scope:"row",colSpan:3},t))},gv=function(){return l.createElement(gg,null,"...")},gy=function(e){var t=e.children;return l.createElement(gg,null,t)},gw=function(e){var t=e.loading,n=e.toml,r=e.error,i=void 0===r?"":r,a=e.title,o=e.expanded;if(i)return l.createElement(gy,null,i);if(t)return l.createElement(gv,null);a||(a="TOML");var s={display:"block"};return l.createElement(x.default,null,l.createElement(mP.Z,{defaultExpanded:o},l.createElement(mR.Z,{expandIcon:l.createElement(gh.Z,null)},a),l.createElement(mj.Z,{style:s},l.createElement(gd,{language:"toml",style:gs},n))))},g_=function(){var e=rv(gm,{fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return(null==t?void 0:t.configv2.effective)=="N/A"?l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12},l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"TOML Configuration"}),l.createElement(gw,{title:"V2 config dump:",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.user,showHead:!0})))):l.createElement(l.Fragment,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"TOML Configuration"}),l.createElement(gw,{title:"User specified:",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.user,showHead:!0,expanded:!0}),l.createElement(gw,{title:"Effective (with defaults):",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.effective,showHead:!0})))))},gE=n(34823),gS=function(e){return(0,b.createStyles)({cell:{paddingTop:1.5*e.spacing.unit,paddingBottom:1.5*e.spacing.unit}})},gk=(0,b.withStyles)(gS)(function(e){var t=e.classes,n=(0,A.I0)();(0,l.useEffect)(function(){n((0,ty.DQ)())});var r=(0,A.v9)(gE.N,A.wU);return l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"Node"}),l.createElement(r8.Z,null,l.createElement(r9.Z,null,l.createElement(ir.Z,null,l.createElement(r7.default,{className:t.cell},l.createElement(x.default,null,"Version"),l.createElement(x.default,{variant:"subtitle1",color:"textSecondary"},r.version))),l.createElement(ir.Z,null,l.createElement(r7.default,{className:t.cell},l.createElement(x.default,null,"SHA"),l.createElement(x.default,{variant:"subtitle1",color:"textSecondary"},r.commitSHA))))))}),gx=function(){return l.createElement(ig,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,sm:12,md:8},l.createElement(d.Z,{container:!0},l.createElement(g_,null))),l.createElement(d.Z,{item:!0,sm:12,md:4},l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(gk,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(mN,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(mE,null))))))},gT=function(){return l.createElement(gx,null)},gM=function(){return l.createElement(gT,null)},gO=n(44431),gA=1e18,gL=function(e){return new gO.BigNumber(e).dividedBy(gA).toFixed(8)},gC=function(e){var t=e.keys,n=e.chainID,r=e.hideHeaderTitle;return l.createElement(l.Fragment,null,l.createElement(sl.Z,{title:!r&&"Account Balances",subheader:"Chain ID "+n}),l.createElement(aW.Z,null,l.createElement(w.default,{dense:!1,disablePadding:!0},t&&t.map(function(e,r){return l.createElement(l.Fragment,null,l.createElement(_.default,{disableGutters:!0,key:["acc-balance",n.toString(),r.toString()].join("-")},l.createElement(E.Z,{primary:l.createElement(l.Fragment,null,l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12},l.createElement(op,{title:"Address"}),l.createElement(ob,{value:e.address})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(op,{title:"Native Token Balance"}),l.createElement(ob,{value:e.ethBalance||"--"})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(op,{title:"LINK Balance"}),l.createElement(ob,{value:e.linkBalance?gL(e.linkBalance):"--"}))))})),r+1s&&l.createElement(gB.Z,null,l.createElement(ir.Z,null,l.createElement(r7.default,{className:r.footer},l.createElement(aA.Z,{href:"/runs",component:tz},"View More"))))))});function vt(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vn(){var e=vt(["\n ","\n query FetchRecentJobRuns($offset: Int, $limit: Int) {\n jobRuns(offset: $offset, limit: $limit) {\n results {\n ...RecentJobRunsPayload_ResultsFields\n }\n metadata {\n total\n }\n }\n }\n"]);return vn=function(){return e},e}var vr=5,vi=n0(vn(),g9),va=function(){var e=rv(vi,{variables:{offset:0,limit:vr},fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return l.createElement(ve,{data:t,errorMsg:null==r?void 0:r.message,loading:n,maxRunsSize:vr})},vo=function(e){return(0,b.createStyles)({style:{textAlign:"center",padding:2.5*e.spacing.unit,position:"fixed",left:"0",bottom:"0",width:"100%",borderRadius:0},bareAnchor:{color:e.palette.common.black,textDecoration:"none"}})},vs=(0,b.withStyles)(vo)(function(e){var t=e.classes,n=(0,A.v9)(gE.N,A.wU),r=(0,A.I0)();return(0,l.useEffect)(function(){r((0,ty.DQ)())}),l.createElement(ii.default,{className:t.style},l.createElement(x.default,null,"Chainlink Node ",n.version," at commit"," ",l.createElement("a",{target:"_blank",rel:"noopener noreferrer",href:"https://github.com/smartcontractkit/chainlink/commit/".concat(n.commitSHA),className:t.bareAnchor},n.commitSHA)))}),vu=function(e){return(0,b.createStyles)({cell:{borderColor:e.palette.divider,borderTop:"1px solid",borderBottom:"none",paddingTop:2*e.spacing.unit,paddingBottom:2*e.spacing.unit,paddingLeft:2*e.spacing.unit},block:{display:"block"},overflowEllipsis:{textOverflow:"ellipsis",overflow:"hidden"}})},vc=(0,b.withStyles)(vu)(function(e){var t=e.classes,n=e.job;return l.createElement(ir.Z,null,l.createElement(r7.default,{scope:"row",className:t.cell},l.createElement(d.Z,{container:!0,spacing:0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(ih,{href:"/jobs/".concat(n.id),classes:{linkContent:t.block}},l.createElement(x.default,{className:t.overflowEllipsis,variant:"body1",component:"span",color:"primary"},n.name||n.id))),l.createElement(d.Z,{item:!0,xs:12},l.createElement(x.default,{variant:"body1",color:"textSecondary"},"Created ",l.createElement(aO,{tooltip:!0},n.createdAt))))))});function vl(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vf(){var e=vl(["\n fragment RecentJobsPayload_ResultsFields on Job {\n id\n name\n createdAt\n }\n"]);return vf=function(){return e},e}var vd=n0(vf()),vh=function(){return(0,b.createStyles)({cardHeader:{borderBottom:0},table:{tableLayout:"fixed"}})},vp=(0,b.withStyles)(vh)(function(e){var t,n,r=e.classes,i=e.data,a=e.errorMsg,o=e.loading;return l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"Recent Jobs",className:r.cardHeader}),l.createElement(r8.Z,{className:r.table},l.createElement(r9.Z,null,l.createElement(g$,{visible:o}),l.createElement(gz,{visible:(null===(t=null==i?void 0:i.jobs.results)||void 0===t?void 0:t.length)===0},"No recently created jobs"),l.createElement(gU,{msg:a}),null===(n=null==i?void 0:i.jobs.results)||void 0===n?void 0:n.map(function(e,t){return l.createElement(vc,{job:e,key:t})}))))});function vb(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vm(){var e=vb(["\n ","\n query FetchRecentJobs($offset: Int, $limit: Int) {\n jobs(offset: $offset, limit: $limit) {\n results {\n ...RecentJobsPayload_ResultsFields\n }\n }\n }\n"]);return vm=function(){return e},e}var vg=5,vv=n0(vm(),vd),vy=function(){var e=rv(vv,{variables:{offset:0,limit:vg},fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return l.createElement(vp,{data:t,errorMsg:null==r?void 0:r.message,loading:n})},vw=function(){return l.createElement(ig,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:8},l.createElement(va,null)),l.createElement(d.Z,{item:!0,xs:4},l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(gY,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(vy,null))))),l.createElement(vs,null))},v_=function(){return l.createElement(vw,null)},vE=function(){return l.createElement(v_,null)},vS=n(87239),vk=function(e){switch(e){case"DirectRequestSpec":return"Direct Request";case"FluxMonitorSpec":return"Flux Monitor";default:return e.replace(/Spec$/,"")}},vx=n(5022),vT=n(78718),vM=n.n(vT);function vO(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n1?t-1:0),r=1;r1?t-1:0),r=1;re.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&n.map(function(e){return l.createElement(ir.Z,{key:e.id,style:{cursor:"pointer"},onClick:function(){return r.push("/runs/".concat(e.id))}},l.createElement(r7.default,{className:t.idCell,scope:"row"},l.createElement("div",{className:t.runDetails},l.createElement(x.default,{variant:"h5",color:"primary",component:"span"},e.id))),l.createElement(r7.default,{className:t.stampCell},l.createElement(x.default,{variant:"body1",color:"textSecondary",className:t.stamp},"Created ",l.createElement(aO,{tooltip:!0},e.createdAt))),l.createElement(r7.default,{className:t.statusCell,scope:"row"},l.createElement(x.default,{variant:"body1",className:O()(t.status,yh(t,e.status))},e.status.toLowerCase())))})))}),yb=n(16839),ym=n.n(yb);function yg(e){var t=e.replace(/\w+\s*=\s*<([^>]|[\r\n])*>/g,""),n=ym().read(t),r=n.edges();return n.nodes().map(function(e){var t={id:e,parentIds:r.filter(function(t){return t.w===e}).map(function(e){return e.v})};return Object.keys(n.node(e)).length>0&&(t.attributes=n.node(e)),t})}var yv=n(94164),yy=function(e){var t=e.data,n=[];return(null==t?void 0:t.attributes)&&Object.keys(t.attributes).forEach(function(e){var r;n.push(l.createElement("div",{key:e},l.createElement(x.default,{variant:"body1",color:"textSecondary",component:"div"},l.createElement("b",null,e,":")," ",null===(r=t.attributes)||void 0===r?void 0:r[e])))}),l.createElement("div",null,t&&l.createElement(x.default,{variant:"body1",color:"textPrimary"},l.createElement("b",null,t.id)),n)},yw=n(73343),y_=n(3379),yE=n.n(y_);function yS(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);nwindow.innerWidth?u-r.getBoundingClientRect().width-a:u+a,n=c+r.getBoundingClientRect().height+i>window.innerHeight?c-r.getBoundingClientRect().height-a:c+a,r.style.opacity=String(1),r.style.top="".concat(n,"px"),r.style.left="".concat(t,"px"),r.style.zIndex=String(1)}},h=function(e){var t=document.getElementById("tooltip-d3-chart-".concat(e));t&&(t.style.opacity=String(0),t.style.zIndex=String(-1))};return l.createElement("div",{style:{fontFamily:"sans-serif",fontWeight:"normal"}},l.createElement(yv.kJ,{id:"task-list-graph-d3",data:i,config:s,onMouseOverNode:d,onMouseOutNode:h},"D3 chart"),n.map(function(e){return l.createElement("div",{key:"d3-tooltip-key-".concat(e.id),id:"tooltip-d3-chart-".concat(e.id),style:{position:"absolute",opacity:"0",border:"1px solid rgba(0, 0, 0, 0.1)",padding:yw.r.spacing.unit,background:"white",borderRadius:5,zIndex:-1,inlineSize:"min-content"}},l.createElement(yy,{data:e}))}))};function yL(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);nyY&&l.createElement("div",{className:t.runDetails},l.createElement(aA.Z,{href:"/jobs/".concat(n.id,"/runs"),component:tz},"View more")))),l.createElement(d.Z,{item:!0,xs:12,sm:6},l.createElement(yF,{observationSource:n.observationSource})))});function yH(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&void 0!==arguments[0]?arguments[0]:"";try{return vx.parse(e),!0}catch(t){return!1}})}),wW=function(e){var t=e.initialValues,n=e.onSubmit,r=e.onTOMLChange;return l.createElement(hT,{initialValues:t,validationSchema:wG,onSubmit:n},function(e){var t=e.isSubmitting,n=e.values;return r&&r(n.toml),l.createElement(hR,{"data-testid":"job-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:hX,id:"toml",name:"toml",label:"Job Spec (TOML)",required:!0,fullWidth:!0,multiline:!0,rows:10,rowsMax:25,variant:"outlined",autoComplete:"off",FormHelperTextProps:{"data-testid":"toml-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(ok.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},"Create Job"))))})},wK=n(50109),wV="persistSpec";function wq(e){var t=e.query,n=new URLSearchParams(t).get("definition");return n?(wK.t8(wV,n),{toml:n}):{toml:wK.U2(wV)||""}}var wZ=function(e){var t=e.onSubmit,n=e.onTOMLChange,r=wq({query:(0,h.TH)().search}),i=function(e){var t=e.replace(/[\u200B-\u200D\uFEFF]/g,"");wK.t8("".concat(wV),t),n&&n(t)};return l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"New Job"}),l.createElement(aW.Z,null,l.createElement(wW,{initialValues:r,onSubmit:t,onTOMLChange:i})))};function wX(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n1&&void 0!==arguments[1]?arguments[1]:{},n=t.start,r=void 0===n?6:n,i=t.end,a=void 0===i?4:i;return e.substring(0,r)+"..."+e.substring(e.length-a)}function _M(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(_W,e)},_V=function(){var e=_K({fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error,i=e.refetch;return l.createElement(_U,{loading:n,data:t,errorMsg:null==r?void 0:r.message,refetch:i})},_q=function(e){var t=e.csaKey;return l.createElement(ir.Z,{hover:!0},l.createElement(r7.default,null,l.createElement(x.default,{variant:"body1"},t.publicKey," ",l.createElement(_x,{data:t.publicKey}))))};function _Z(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function _X(){var e=_Z(["\n fragment CSAKeysPayload_ResultsFields on CSAKey {\n id\n publicKey\n }\n"]);return _X=function(){return e},e}var _J=n0(_X()),_Q=function(e){var t,n,r,i=e.data,a=e.errorMsg,o=e.loading,s=e.onCreate;return l.createElement(r5.Z,null,l.createElement(sl.Z,{action:(null===(t=null==i?void 0:i.csaKeys.results)||void 0===t?void 0:t.length)===0&&l.createElement(ok.default,{variant:"outlined",color:"primary",onClick:s},"New CSA Key"),title:"CSA Key",subheader:"Manage your CSA Key"}),l.createElement(r8.Z,null,l.createElement(ie.Z,null,l.createElement(ir.Z,null,l.createElement(r7.default,null,"Public Key"))),l.createElement(r9.Z,null,l.createElement(g$,{visible:o}),l.createElement(gz,{visible:(null===(n=null==i?void 0:i.csaKeys.results)||void 0===n?void 0:n.length)===0}),l.createElement(gU,{msg:a}),null===(r=null==i?void 0:i.csaKeys.results)||void 0===r?void 0:r.map(function(e,t){return l.createElement(_q,{csaKey:e,key:t})}))))};function _1(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(EM,e)};function EA(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(EJ,e)},E3=function(){return oo(EQ)},E4=function(){return oo(E1)},E6=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return rv(E0,e)};function E5(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(SK,e)};function Sq(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function kV(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}var kq=function(e){var t=e.run,n=l.useMemo(function(){var e=t.inputs,n=t.outputs,r=t.taskRuns,i=kK(t,["inputs","outputs","taskRuns"]),a={};try{a=JSON.parse(e)}catch(o){a={}}return kW(kz({},i),{inputs:a,outputs:n,taskRuns:r})},[t]);return l.createElement(r5.Z,null,l.createElement(aW.Z,null,l.createElement(kH,{object:n})))};function kZ(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function kX(e){for(var t=1;t0&&l.createElement(kr,{errors:t.allErrors})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(h.rs,null,l.createElement(h.AW,{path:"".concat(n,"/json")},l.createElement(kq,{run:t})),l.createElement(h.AW,{path:n},t.taskRuns.length>0&&l.createElement(kN,{taskRuns:t.taskRuns,observationSource:t.job.observationSource}))))))))};function k5(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function k8(){var e=k5(["\n ","\n query FetchJobRun($id: ID!) {\n jobRun(id: $id) {\n __typename\n ... on JobRun {\n ...JobRunPayload_Fields\n }\n ... on NotFoundError {\n message\n }\n }\n }\n"]);return k8=function(){return e},e}var k9=n0(k8(),k4),k7=function(){var e=rv(k9,{variables:{id:(0,h.UO)().id}}),t=e.data,n=e.loading,r=e.error;if(n)return l.createElement(iR,null);if(r)return l.createElement(iD,{error:r});var i=null==t?void 0:t.jobRun;switch(null==i?void 0:i.__typename){case"JobRun":return l.createElement(k6,{run:i});case"NotFoundError":return l.createElement(oa,null);default:return null}};function xe(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xt(){var e=xe(["\n fragment JobRunsPayload_ResultsFields on JobRun {\n id\n allErrors\n createdAt\n finishedAt\n status\n job {\n id\n }\n }\n"]);return xt=function(){return e},e}var xn=n0(xt()),xr=function(e){var t=e.loading,n=e.data,r=e.page,i=e.pageSize,a=(0,h.k6)(),o=l.useMemo(function(){return null==n?void 0:n.jobRuns.results.map(function(e){var t,n=e.allErrors,r=e.id,i=e.createdAt;return{id:r,createdAt:i,errors:n,finishedAt:e.finishedAt,status:e.status}})},[n]);return l.createElement(ig,null,l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:12},l.createElement(iy,null,"Job Runs")),t&&l.createElement(iR,null),n&&o&&l.createElement(d.Z,{item:!0,xs:12},l.createElement(r5.Z,null,l.createElement(yp,{runs:o}),l.createElement(it.Z,{component:"div",count:n.jobRuns.metadata.total,rowsPerPage:i,rowsPerPageOptions:[i],page:r-1,onChangePage:function(e,t){a.push("/runs?page=".concat(t+1,"&per=").concat(i))},onChangeRowsPerPage:function(){},backIconButtonProps:{"aria-label":"prev-page"},nextIconButtonProps:{"aria-label":"next-page"}})))))};function xi(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xa(){var e=xi(["\n ","\n query FetchJobRuns($offset: Int, $limit: Int) {\n jobRuns(offset: $offset, limit: $limit) {\n results {\n ...JobRunsPayload_ResultsFields\n }\n metadata {\n total\n }\n }\n }\n"]);return xa=function(){return e},e}var xo=n0(xa(),xn),xs=function(){var e=ij(),t=parseInt(e.get("page")||"1",10),n=parseInt(e.get("per")||"25",10),r=rv(xo,{variables:{offset:(t-1)*n,limit:n},fetchPolicy:"cache-and-network"}),i=r.data,a=r.loading,o=r.error;return o?l.createElement(iD,{error:o}):l.createElement(xr,{loading:a,data:i,page:t,pageSize:n})},xu=function(){var e=(0,h.$B)().path;return l.createElement(h.rs,null,l.createElement(h.AW,{exact:!0,path:e},l.createElement(xs,null)),l.createElement(h.AW,{path:"".concat(e,"/:id")},l.createElement(k7,null)))};function xc(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xl(){var e=xc(["\n fragment FetchFeedsManagersPayload_ResultsFields on FeedsManager {\n __typename\n id\n name\n uri\n publicKey\n isConnectionActive\n createdAt\n }\n query FetchFeedsManagers {\n feedsManagers {\n results {\n ...FetchFeedsManagersPayload_ResultsFields\n }\n }\n }\n"]);return xl=function(){return e},e}var xf=n0(xl()),xd=function(e){return rv(xf,e)},xh=n(47559),xp=n(83165),xb=n(47298),xm=n(81395),xg=function(){return(0,b.createStyles)({root:{display:"flex"},connectedIcon:{color:xh.default[500]},disconnectedIcon:{color:xp.default[500]},text:{marginLeft:4}})},xv=(0,b.withStyles)(xg)(function(e){var t=e.isConnected,n=e.classes;return l.createElement("div",{className:n.root},t?l.createElement(xm.Z,{fontSize:"small",className:n.connectedIcon}):l.createElement(xb.Z,{fontSize:"small",className:n.disconnectedIcon}),l.createElement(x.default,{variant:"body1",inline:!0,className:n.text},t?"Connected":"Disconnected"))}),xy=(0,b.withStyles)(iu)(function(e){var t=e.jobDistributor,n=e.classes;return l.createElement(ir.Z,{className:n.row,hover:!0},l.createElement(r7.default,{className:n.cell,component:"th",scope:"row"},l.createElement(ih,{className:n.link,href:"/job_distributors/".concat(t.id)},t.name)),l.createElement(r7.default,null,l.createElement(xv,{isConnected:t.isConnectionActive})),l.createElement(r7.default,null,_T(t.publicKey,{start:6,end:6}),l.createElement(_x,{data:t.publicKey})),l.createElement(r7.default,null,t.uri))}),xw=function(e){var t=e.jobDistributors;return l.createElement(ig,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:9},l.createElement(iy,null,"Job Distributors")),l.createElement(d.Z,{item:!0,xs:3},l.createElement(d.Z,{container:!0,justify:"flex-end"},l.createElement(d.Z,{item:!0},l.createElement(aA.Z,{variant:"secondary",component:tz,href:"/job_distributors/new"},"New Job Distributor")))),l.createElement(d.Z,{item:!0,xs:12},l.createElement(r5.Z,null,l.createElement(r8.Z,null,l.createElement(ie.Z,null,l.createElement(ir.Z,null,l.createElement(r7.default,null,"Name"),l.createElement(r7.default,null,"Status"),l.createElement(r7.default,null,"CSA Public Key"),l.createElement(r7.default,null,"RPC URL"))),l.createElement(r9.Z,null,0===t.length&&l.createElement(ir.Z,null,l.createElement(r7.default,{component:"th",scope:"row",colSpan:3},"Job Distributors have not been registered")),t.map(function(e){return l.createElement(xy,{key:e.id,jobDistributor:e})})))))))},x_=function(){var e,t=xd({fetchPolicy:"cache-and-network"}),n=t.data,r=t.loading,i=t.error;return r?l.createElement(iR,null):i?l.createElement(iD,{error:i}):l.createElement(xw,{jobDistributors:null!==(e=null==n?void 0:n.feedsManagers.results)&&void 0!==e?e:[]})},xE=bv().shape({name:p0().required("Required"),uri:p0().required("Required"),publicKey:p0().required("Required")}),xS=function(e){var t=e.initialValues,n=e.onSubmit;return l.createElement(hT,{initialValues:t,validationSchema:xE,onSubmit:n},function(e){var t=e.isSubmitting,n=e.submitForm;return l.createElement(hR,{"data-testid":"feeds-manager-form"},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"name",name:"name",label:"Name",required:!0,fullWidth:!0,FormHelperTextProps:{"data-testid":"name-helper-text"}})),l.createElement(d.Z,{item:!0,xs:!1,md:6}),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"uri",name:"uri",label:"URI",required:!0,fullWidth:!0,helperText:"Provided by the Job Distributor operator",FormHelperTextProps:{"data-testid":"uri-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"publicKey",name:"publicKey",label:"Public Key",required:!0,fullWidth:!0,helperText:"Provided by the Job Distributor operator",FormHelperTextProps:{"data-testid":"publicKey-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(ok.default,{variant:"contained",color:"primary",disabled:t,onClick:n},"Submit"))))})},xk=function(e){var t=e.data,n=e.onSubmit,r={name:t.name,uri:t.uri,publicKey:t.publicKey};return l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"Edit Job Distributor"}),l.createElement(aW.Z,null,l.createElement(xS,{initialValues:r,onSubmit:n})))))};function xx(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(xZ,e)},xJ=n(57234);function xQ(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function x6(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}function x5(e,t){return x1(e)||x2(e,t)||x8(e,t)||x3()}function x8(e,t){if(e){if("string"==typeof e)return xQ(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);if("Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n)return Array.from(n);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return xQ(e,t)}}var x9=function(e){return"SN_MAIN"===e||"SN_SEPOLIA"===e},x7=bv().shape({chainID:p0().required("Required"),chainType:p0().required("Required"),accountAddr:p0().required("Required"),accountAddrPubKey:p0().nullable(),adminAddr:p0().required("Required"),ocr1Multiaddr:p0().when(["ocr1Enabled","ocr1IsBootstrap"],{is:function(e,t){return e&&t},then:p0().required("Required").nullable()}).nullable(),ocr1P2PPeerID:p0().when(["ocr1Enabled","ocr1IsBootstrap"],{is:function(e,t){return e&&!t},then:p0().required("Required").nullable()}).nullable(),ocr1KeyBundleID:p0().when(["ocr1Enabled","ocr1IsBootstrap"],{is:function(e,t){return e&&!t},then:p0().required("Required").nullable()}).nullable(),ocr2Multiaddr:p0().when(["ocr2Enabled","ocr2IsBootstrap"],{is:function(e,t){return e&&t},then:p0().required("Required").nullable()}).nullable(),ocr2P2PPeerID:p0().when(["ocr2Enabled","ocr2IsBootstrap"],{is:function(e,t){return e&&!t},then:p0().required("Required").nullable()}).nullable(),ocr2KeyBundleID:p0().when(["ocr2Enabled","ocr2IsBootstrap"],{is:function(e,t){return e&&!t},then:p0().required("Required").nullable()}).nullable(),ocr2CommitPluginEnabled:pV().required("Required"),ocr2ExecutePluginEnabled:pV().required("Required"),ocr2MedianPluginEnabled:pV().required("Required"),ocr2MercuryPluginEnabled:pV().required("Required"),ocr2ForwarderAddress:p0().nullable()}),Te=function(e){return(0,b.createStyles)({supportedJobOptionsPaper:{padding:2*e.spacing.unit}})},Tt=function(e){var t=e.chainAccounts,n=x4(e,["chainAccounts"]),r=h_(),i=r.values,a=i.chainID,o=i.accountAddr,s=r.setFieldValue,u=x5(l.useState(!1),2),c=u[0],f=u[1],d=l.useRef();l.useEffect(function(){d.current=a},[a]),l.useEffect(function(){a!==d.current&&(s(n.name,""),f(!1))},[a,s,n.name]);var h=function(e){var t=e.target.value;"custom"===t?(f(!0),s(n.name,"")):(f(!1),s(n.name,t))};return l.createElement(l.Fragment,null,!x9(a)&&l.createElement(hP,x0({},n,{select:!0,value:c?"custom":o,onChange:h}),t.map(function(e){return l.createElement(tE.default,{key:e.address,value:e.address},e.address)})),x9(a)&&l.createElement(hP,{component:hX,id:"accountAddr",name:"accountAddr",label:"Enter your account address",inputProps:{"data-testid":"customAccountAddr-input"},helperText:"The account address used for this chain",required:!0,fullWidth:!0}),x9(a)&&l.createElement("div",null,l.createElement(hP,{component:hX,id:"accountAddrPubKey",name:"accountAddrPubKey",label:"Account Address Public Key",required:!0,fullWidth:!0,helperText:"The public key for your account address",FormHelperTextProps:{"data-testid":"accountAddrPubKey-helper-text"}})))},Tn=(0,b.withStyles)(Te)(function(e){var t=e.classes,n=e.editing,r=void 0!==n&&n,i=e.innerRef,a=e.initialValues,o=e.onSubmit,s=e.chainIDs,u=void 0===s?[]:s,c=e.accounts,f=void 0===c?[]:c,h=e.p2pKeys,p=void 0===h?[]:h,b=e.ocrKeys,m=void 0===b?[]:b,g=e.ocr2Keys,v=void 0===g?[]:g,y=e.showSubmit,w=void 0!==y&&y;return l.createElement(hT,{innerRef:i,initialValues:a,validationSchema:x7,onSubmit:o},function(e){var n=e.values,i=f.filter(function(e){return e.chain.id==n.chainID&&!e.isDisabled});return l.createElement(hR,{"data-testid":"feeds-manager-form",id:"chain-configuration-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"chainType",name:"chainType",label:"Chain Type",select:!0,required:!0,fullWidth:!0,disabled:!0},l.createElement(tE.default,{key:"EVM",value:"EVM"},"EVM"))),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"chainID",name:"chainID",label:"Chain ID",required:!0,fullWidth:!0,select:!0,disabled:r,inputProps:{"data-testid":"chainID-input"},FormHelperTextProps:{"data-testid":"chainID-helper-text"}},u.map(function(e){return l.createElement(tE.default,{key:e,value:e},e)}))),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(Tt,{component:hX,id:"accountAddr",name:"accountAddr",label:"Account Address",inputProps:{"data-testid":"accountAddr-input"},required:!0,fullWidth:!0,select:!0,helperText:"The account address used for this chain",chainAccounts:i,FormHelperTextProps:{"data-testid":"accountAddr-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"adminAddr",name:"adminAddr",label:"Admin Address",required:!0,fullWidth:!0,helperText:"The address used for LINK payments",FormHelperTextProps:{"data-testid":"adminAddr-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(x.default,null,"Supported Job Types")),l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"fluxMonitorEnabled",type:"checkbox",Label:{label:"Flux Monitor"}})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"ocr1Enabled",type:"checkbox",Label:{label:"OCR"}}),n.ocr1Enabled&&l.createElement(ii.default,{className:t.supportedJobOptionsPaper},l.createElement(d.Z,{container:!0,spacing:8},l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"ocr1IsBootstrap",type:"checkbox",Label:{label:"Is this node running as a bootstrap peer?"}})),n.ocr1IsBootstrap?l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:hX,id:"ocr1Multiaddr",name:"ocr1Multiaddr",label:"Multiaddr",required:!0,fullWidth:!0,helperText:"The OCR Multiaddr which oracles use to query for network information",FormHelperTextProps:{"data-testid":"ocr1Multiaddr-helper-text"}})):l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"ocr1P2PPeerID",name:"ocr1P2PPeerID",label:"Peer ID",select:!0,required:!0,fullWidth:!0,helperText:"The Peer ID used for this chain",FormHelperTextProps:{"data-testid":"ocr1P2PPeerID-helper-text"}},p.map(function(e){return l.createElement(tE.default,{key:e.peerID,value:e.peerID},e.peerID)}))),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"ocr1KeyBundleID",name:"ocr1KeyBundleID",label:"Key Bundle ID",select:!0,required:!0,fullWidth:!0,helperText:"The OCR Key Bundle ID used for this chain",FormHelperTextProps:{"data-testid":"ocr1KeyBundleID-helper-text"}},m.map(function(e){return l.createElement(tE.default,{key:e.id,value:e.id},e.id)})))))))),l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"ocr2Enabled",type:"checkbox",Label:{label:"OCR2"}}),n.ocr2Enabled&&l.createElement(ii.default,{className:t.supportedJobOptionsPaper},l.createElement(d.Z,{container:!0,spacing:8},l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"ocr2IsBootstrap",type:"checkbox",Label:{label:"Is this node running as a bootstrap peer?"}})),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"ocr2P2PPeerID",name:"ocr2P2PPeerID",label:"Peer ID",select:!0,required:!n.ocr2IsBootstrap,fullWidth:!0,helperText:"The Peer ID used for this chain",FormHelperTextProps:{"data-testid":"ocr2P2PPeerID-helper-text"}},p.map(function(e){return l.createElement(tE.default,{key:e.peerID,value:e.peerID},e.peerID)}))),n.ocr2IsBootstrap?l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:hX,id:"ocr2Multiaddr",name:"ocr2Multiaddr",label:"Multiaddr",required:!0,fullWidth:!0,helperText:"The OCR2 Multiaddr which oracles use to query for network information",FormHelperTextProps:{"data-testid":"ocr2Multiaddr-helper-text"}})):l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"ocr2KeyBundleID",name:"ocr2KeyBundleID",label:"Key Bundle ID",select:!0,required:!0,fullWidth:!0,helperText:"The OCR2 Key Bundle ID used for this chain",FormHelperTextProps:{"data-testid":"ocr2KeyBundleID-helper-text"}},v.map(function(e){return l.createElement(tE.default,{key:e.id,value:e.id},e.id)}))),l.createElement(d.Z,{item:!0,xs:12},l.createElement(x.default,null,"Supported Plugins")),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2CommitPluginEnabled",type:"checkbox",Label:{label:"Commit"}})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2ExecutePluginEnabled",type:"checkbox",Label:{label:"Execute"}})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2RebalancerPluginEnabled",type:"checkbox",Label:{label:"Rebalancer"}})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2MedianPluginEnabled",type:"checkbox",Label:{label:"Median"}})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2MercuryPluginEnabled",type:"checkbox",Label:{label:"Mercury"}})),l.createElement(d.Z,{item:!0,xs:12,md:12},l.createElement(hP,{component:hX,id:"ocr2ForwarderAddress",name:"ocr2ForwarderAddress",label:"Forwarder Address (optional)",fullWidth:!0,helperText:"The forwarder address from the Operator Forwarder Contract",FormHelperTextProps:{"data-testid":"ocr2ForwarderAddress-helper-text"}}))))))),w&&l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(ok.default,{variant:"contained",color:"primary",type:"submit",size:"large"},"Submit"))))})}),Tr=function(e){var t=e.onClose,n=e.open,r=e.onSubmit,i=l.useRef(),a=i$({fetchPolicy:"network-only"}).data,o=_K({fetchPolicy:"cache-and-network"}).data,s=SV({fetchPolicy:"cache-and-network"}).data,u=EO({fetchPolicy:"cache-and-network"}).data,c=E2({fetchPolicy:"cache-and-network"}).data,f={chainID:"",chainType:"EVM",accountAddr:"",adminAddr:"",accountAddrPubKey:"",fluxMonitorEnabled:!1,ocr1Enabled:!1,ocr1IsBootstrap:!1,ocr1Multiaddr:"",ocr1P2PPeerID:"",ocr1KeyBundleID:"",ocr2Enabled:!1,ocr2IsBootstrap:!1,ocr2Multiaddr:"",ocr2P2PPeerID:"",ocr2KeyBundleID:"",ocr2CommitPluginEnabled:!1,ocr2ExecutePluginEnabled:!1,ocr2MedianPluginEnabled:!1,ocr2MercuryPluginEnabled:!1,ocr2RebalancerPluginEnabled:!1,ocr2ForwarderAddress:""},d=a?a.chains.results.map(function(e){return e.id}):[],h=o?o.ethKeys.results:[],p=s?s.p2pKeys.results:[],b=u?u.ocrKeyBundles.results:[],m=c?c.ocr2KeyBundles.results:[];return l.createElement(aD.Z,{onClose:t,open:n,disableBackdropClick:!0},l.createElement(oO.Z,{disableTypography:!0},l.createElement(x.default,{variant:"body2"},"New Supported Chain")),l.createElement(oT.Z,null,l.createElement(Tn,{innerRef:i,initialValues:f,onSubmit:r,chainIDs:d,accounts:h,p2pKeys:p,ocrKeys:b,ocr2Keys:m})),l.createElement(ox.Z,null,l.createElement(ok.default,{onClick:t},"Cancel"),l.createElement(ok.default,{color:"primary",type:"submit",form:"chain-configuration-form",variant:"contained"},"Submit")))},Ti=function(e){var t=e.cfg,n=e.onClose,r=e.open,i=e.onSubmit,a=l.useRef(),o=i$({fetchPolicy:"network-only"}).data,s=_K({fetchPolicy:"cache-and-network"}).data,u=SV({fetchPolicy:"cache-and-network"}).data,c=EO({fetchPolicy:"cache-and-network"}).data,f=E2({fetchPolicy:"cache-and-network"}).data;if(!t)return null;var d={chainID:t.chainID,chainType:"EVM",accountAddr:t.accountAddr,adminAddr:t.adminAddr,accountAddrPubKey:t.accountAddrPubKey,fluxMonitorEnabled:t.fluxMonitorJobConfig.enabled,ocr1Enabled:t.ocr1JobConfig.enabled,ocr1IsBootstrap:t.ocr1JobConfig.isBootstrap,ocr1Multiaddr:t.ocr1JobConfig.multiaddr,ocr1P2PPeerID:t.ocr1JobConfig.p2pPeerID,ocr1KeyBundleID:t.ocr1JobConfig.keyBundleID,ocr2Enabled:t.ocr2JobConfig.enabled,ocr2IsBootstrap:t.ocr2JobConfig.isBootstrap,ocr2Multiaddr:t.ocr2JobConfig.multiaddr,ocr2P2PPeerID:t.ocr2JobConfig.p2pPeerID,ocr2KeyBundleID:t.ocr2JobConfig.keyBundleID,ocr2CommitPluginEnabled:t.ocr2JobConfig.plugins.commit,ocr2ExecutePluginEnabled:t.ocr2JobConfig.plugins.execute,ocr2MedianPluginEnabled:t.ocr2JobConfig.plugins.median,ocr2MercuryPluginEnabled:t.ocr2JobConfig.plugins.mercury,ocr2RebalancerPluginEnabled:t.ocr2JobConfig.plugins.rebalancer,ocr2ForwarderAddress:t.ocr2JobConfig.forwarderAddress},h=o?o.chains.results.map(function(e){return e.id}):[],p=s?s.ethKeys.results:[],b=u?u.p2pKeys.results:[],m=c?c.ocrKeyBundles.results:[],g=f?f.ocr2KeyBundles.results:[];return l.createElement(aD.Z,{onClose:n,open:r,disableBackdropClick:!0},l.createElement(oO.Z,{disableTypography:!0},l.createElement(x.default,{variant:"body2"},"Edit Supported Chain")),l.createElement(oT.Z,null,l.createElement(Tn,{innerRef:a,initialValues:d,onSubmit:i,chainIDs:h,accounts:p,p2pKeys:b,ocrKeys:m,ocr2Keys:g,editing:!0})),l.createElement(ox.Z,null,l.createElement(ok.default,{onClick:n},"Cancel"),l.createElement(ok.default,{color:"primary",type:"submit",form:"chain-configuration-form",variant:"contained"},"Submit")))};function Ta(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);nt.version?e:t})},[o]),g=l.useMemo(function(){return Mx(o).sort(function(e,t){return t.version-e.version})},[o]),v=function(e,t,n){switch(e){case"PENDING":return l.createElement(l.Fragment,null,l.createElement(ok.default,{variant:"text",color:"secondary",onClick:function(){return b("reject",t)}},"Reject"),m.id===t&&"DELETED"!==n.status&&"REVOKED"!==n.status&&l.createElement(ok.default,{variant:"contained",color:"primary",onClick:function(){return b("approve",t)}},"Approve"),m.id===t&&"DELETED"===n.status&&n.pendingUpdate&&l.createElement(l.Fragment,null,l.createElement(ok.default,{variant:"contained",color:"primary",onClick:function(){return b("cancel",t)}},"Cancel"),l.createElement(x.default,{color:"error"},"This proposal was deleted. Cancel the spec to delete any running jobs")));case"APPROVED":return l.createElement(l.Fragment,null,l.createElement(ok.default,{variant:"contained",onClick:function(){return b("cancel",t)}},"Cancel"),"DELETED"===n.status&&n.pendingUpdate&&l.createElement(x.default,{color:"error"},"This proposal was deleted. Cancel the spec to delete any running jobs"));case"CANCELLED":if(m.id===t&&"DELETED"!==n.status&&"REVOKED"!==n.status)return l.createElement(ok.default,{variant:"contained",color:"primary",onClick:function(){return b("approve",t)}},"Approve");return null;default:return null}};return l.createElement("div",null,g.map(function(e,n){return l.createElement(mP.Z,{defaultExpanded:0===n,key:n},l.createElement(mR.Z,{expandIcon:l.createElement(gh.Z,null)},l.createElement(x.default,{className:t.versionText},"Version ",e.version),l.createElement(Es.Z,{label:e.status,color:"APPROVED"===e.status?"primary":"default",variant:"REJECTED"===e.status||"CANCELLED"===e.status?"outlined":"default"}),l.createElement("div",{className:t.proposedAtContainer},l.createElement(x.default,null,"Proposed ",l.createElement(aO,{tooltip:!0},e.createdAt)))),l.createElement(mj.Z,{className:t.expansionPanelDetails},l.createElement("div",{className:t.actions},l.createElement("div",{className:t.editContainer},0===n&&("PENDING"===e.status||"CANCELLED"===e.status)&&"DELETED"!==s.status&&"REVOKED"!==s.status&&l.createElement(ok.default,{variant:"contained",onClick:function(){return p(!0)}},"Edit")),l.createElement("div",{className:t.actionsContainer},v(e.status,e.id,s))),l.createElement(gd,{language:"toml",style:gs,"data-testid":"codeblock"},e.definition)))}),l.createElement(oC,{open:null!=c,title:c?ML[c.action].title:"",body:c?ML[c.action].body:"",onConfirm:function(){if(c){switch(c.action){case"approve":n(c.id);break;case"cancel":r(c.id);break;case"reject":i(c.id)}f(null)}},cancelButtonText:"Cancel",onCancel:function(){return f(null)}}),l.createElement(Mb,{open:h,onClose:function(){return p(!1)},initialValues:{definition:m.definition,id:m.id},onSubmit:a}))});function MI(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function MD(){var e=MI(["\n ","\n fragment JobProposalPayloadFields on JobProposal {\n id\n externalJobID\n remoteUUID\n jobID\n specs {\n ...JobProposal_SpecsFields\n }\n status\n pendingUpdate\n }\n"]);return MD=function(){return e},e}var MN=n0(MD(),MO),MP=function(e){var t=e.onApprove,n=e.onCancel,r=e.onReject,i=e.onUpdateSpec,a=e.proposal;return l.createElement(ig,null,l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:9},l.createElement(iy,null,"Job Proposal #",a.id))),l.createElement(Mc,{proposal:a}),l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:9},l.createElement(TJ,null,"Specs"))),l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:12},l.createElement(MC,{proposal:a,specs:a.specs,onReject:r,onApprove:t,onCancel:n,onUpdateSpec:i}))))};function MR(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);nU,tA:()=>$,KL:()=>H,Iw:()=>V,DQ:()=>W,cB:()=>T,LO:()=>M,t5:()=>k,qt:()=>x,Jc:()=>C,L7:()=>Y,EO:()=>B});var r,i,a=n(66289),o=n(41800),s=n.n(o),u=n(67932);(i=r||(r={})).IN_PROGRESS="in_progress",i.PENDING_INCOMING_CONFIRMATIONS="pending_incoming_confirmations",i.PENDING_CONNECTION="pending_connection",i.PENDING_BRIDGE="pending_bridge",i.PENDING_SLEEP="pending_sleep",i.ERRORED="errored",i.COMPLETED="completed";var c=n(87013),l=n(19084),f=n(34823);function d(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]j,v2:()=>F});var r=n(66289);function i(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var a="/sessions",o="/sessions",s=function e(t){var n=this;i(this,e),this.api=t,this.createSession=function(e){return n.create(e)},this.destroySession=function(){return n.destroy()},this.create=this.api.createResource(a),this.destroy=this.api.deleteResource(o)};function u(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var c="/v2/bulk_delete_runs",l=function e(t){var n=this;u(this,e),this.api=t,this.bulkDeleteJobRuns=function(e){return n.destroy(e)},this.destroy=this.api.deleteResource(c)};function f(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var d="/v2/chains/evm",h="".concat(d,"/:id"),p=function e(t){var n=this;f(this,e),this.api=t,this.getChains=function(){return n.index()},this.createChain=function(e){return n.create(e)},this.destroyChain=function(e){return n.destroy(void 0,{id:e})},this.updateChain=function(e,t){return n.update(t,{id:e})},this.index=this.api.fetchResource(d),this.create=this.api.createResource(d),this.destroy=this.api.deleteResource(h),this.update=this.api.updateResource(h)};function b(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var m="/v2/keys/evm/chain",g=function e(t){var n=this;b(this,e),this.api=t,this.chain=function(e){var t=new URLSearchParams;t.append("address",e.address),t.append("evmChainID",e.evmChainID),null!==e.nextNonce&&t.append("nextNonce",e.nextNonce),null!==e.abandon&&t.append("abandon",String(e.abandon)),null!==e.enabled&&t.append("enabled",String(e.enabled));var r=m+"?"+t.toString();return n.api.createResource(r)()}};function v(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var y="/v2/jobs",w="".concat(y,"/:specId/runs"),_=function e(t){var n=this;v(this,e),this.api=t,this.createJobRunV2=function(e,t){return n.post(t,{specId:e})},this.post=this.api.createResource(w,!0)};function E(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var S="/v2/log",k=function e(t){var n=this;E(this,e),this.api=t,this.getLogConfig=function(){return n.show()},this.updateLogConfig=function(e){return n.update(e)},this.show=this.api.fetchResource(S),this.update=this.api.updateResource(S)};function x(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var T="/v2/nodes",M=function e(t){var n=this;x(this,e),this.api=t,this.getNodes=function(){return n.index()},this.createNode=function(e){return n.create(e)},this.index=this.api.fetchResource(T),this.create=this.api.createResource(T)};function O(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var A="/v2/enroll_webauthn",L=function e(t){var n=this;O(this,e),this.api=t,this.beginKeyRegistration=function(e){return n.create(e)},this.finishKeyRegistration=function(e){return n.put(e)},this.create=this.api.fetchResource(A),this.put=this.api.createResource(A)};function C(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var I="/v2/build_info",D=function e(t){var n=this;C(this,e),this.api=t,this.show=function(){return n.api.GET(I)()}};function N(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var P=function e(t){N(this,e),this.api=t,this.buildInfo=new D(this.api),this.bulkDeleteRuns=new l(this.api),this.chains=new p(this.api),this.logConfig=new k(this.api),this.nodes=new M(this.api),this.jobs=new _(this.api),this.webauthn=new L(this.api),this.evmKeys=new g(this.api)},R=new r.V0({base:void 0}),j=new s(R),F=new P(R)},1398(e,t,n){"use strict";n.d(t,{Z:()=>d});var r=n(67294),i=n(32316),a=n(83638),o=n(94184),s=n.n(o);function u(){return(u=Object.assign||function(e){for(var t=1;tc});var r=n(67294),i=n(32316);function a(){return(a=Object.assign||function(e){for(var t=1;tx,jK:()=>v});var r=n(67294),i=n(37703),a=n(45697),o=n.n(a),s=n(82204),u=n(71426),c=n(94184),l=n.n(c),f=n(32316),d=function(e){var t=e.palette.success||{},n=e.palette.warning||{};return{base:{paddingLeft:5*e.spacing.unit,paddingRight:5*e.spacing.unit},success:{backgroundColor:t.main,color:t.contrastText},error:{backgroundColor:e.palette.error.dark,color:e.palette.error.contrastText},warning:{backgroundColor:n.contrastText,color:n.main}}},h=function(e){var t,n=e.success,r=e.error,i=e.warning,a=e.classes,o=e.className;return n?t=a.success:r?t=a.error:i&&(t=a.warning),l()(a.base,o,t)},p=function(e){return r.createElement(s.Z,{className:h(e),square:!0},r.createElement(u.default,{variant:"body2",color:"inherit",component:"div"},e.children))};p.defaultProps={success:!1,error:!1,warning:!1},p.propTypes={success:o().bool,error:o().bool,warning:o().bool};let b=(0,f.withStyles)(d)(p);var m=function(){return r.createElement(r.Fragment,null,"Unhandled error. Please help us by opening a"," ",r.createElement("a",{href:"https://github.com/smartcontractkit/chainlink/issues/new"},"bug report"))};let g=m;function v(e){return"string"==typeof e?e:e.component?e.component(e.props):r.createElement(g,null)}function y(e,t){var n;return n="string"==typeof e?e:e.component?e.component(e.props):r.createElement(g,null),r.createElement("p",{key:t},n)}var w=function(e){var t=e.notifications;return r.createElement(b,{error:!0},t.map(y))},_=function(e){var t=e.notifications;return r.createElement(b,{success:!0},t.map(y))},E=function(e){var t=e.errors,n=e.successes;return r.createElement("div",null,(null==t?void 0:t.length)>0&&r.createElement(w,{notifications:t}),n.length>0&&r.createElement(_,{notifications:n}))},S=function(e){return{errors:e.notifications.errors,successes:e.notifications.successes}},k=(0,i.$j)(S)(E);let x=k},9409(e,t,n){"use strict";n.d(t,{ZP:()=>j});var r=n(67294),i=n(37703),a=n(5977),o=n(32316),s=n(1398),u=n(82204),c=n(30060),l=n(71426),f=n(60520),d=n(39814),h=n(57209),p=n(26842),b=n(3950),m=n(5536),g=n(45697),v=n.n(g);let y=n.p+"9f6d832ef97e8493764e.svg";function w(){return(w=Object.assign||function(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&_.map(function(e,t){return r.createElement(d.Z,{item:!0,xs:12,key:t},r.createElement(u.Z,{raised:!1,className:v.error},r.createElement(c.Z,null,r.createElement(l.default,{variant:"body1",className:v.errorText},(0,b.jK)(e)))))}),r.createElement(d.Z,{item:!0,xs:12},r.createElement(f.Z,{id:"email",label:"Email",margin:"normal",value:n,onChange:m("email"),error:_.length>0,variant:"outlined",fullWidth:!0})),r.createElement(d.Z,{item:!0,xs:12},r.createElement(f.Z,{id:"password",label:"Password",type:"password",autoComplete:"password",margin:"normal",value:h,onChange:m("password"),error:_.length>0,variant:"outlined",fullWidth:!0})),r.createElement(d.Z,{item:!0,xs:12},r.createElement(d.Z,{container:!0,spacing:0,justify:"center"},r.createElement(d.Z,{item:!0},r.createElement(s.Z,{type:"submit",variant:"primary"},"Access Account")))),y&&r.createElement(l.default,{variant:"body1",color:"textSecondary"},"Signing in...")))))))},P=function(e){return{fetching:e.authentication.fetching,authenticated:e.authentication.allowed,errors:e.notifications.errors}},R=(0,i.$j)(P,x({submitSignIn:p.L7}))(N);let j=(0,h.wU)(e)((0,o.withStyles)(D)(R))},16353(e,t,n){"use strict";n.d(t,{ZP:()=>H,rH:()=>U});var r,i=n(37703),a=n(97779),o=n(9541),s=n(19084);function u(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function c(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:h,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case s.Mk.RECEIVE_SIGNOUT_SUCCESS:case s.Mk.RECEIVE_SIGNIN_SUCCESS:var n={allowed:t.authenticated};return o.Ks(n),f(c({},e,n),{errors:[]});case s.Mk.RECEIVE_SIGNIN_FAIL:var r={allowed:!1};return o.Ks(r),f(c({},e,r),{errors:[]});case s.Mk.RECEIVE_SIGNIN_ERROR:case s.Mk.RECEIVE_SIGNOUT_ERROR:var i={allowed:!1};return o.Ks(i),f(c({},e,i),{errors:t.errors||[]});default:return e}};let b=p;function m(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function g(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:_,t=arguments.length>1?arguments[1]:void 0;return t.type?t.type.startsWith(r.REQUEST)?y(g({},e),{count:e.count+1}):t.type.startsWith(r.RECEIVE)?y(g({},e),{count:Math.max(e.count-1,0)}):t.type.startsWith(r.RESPONSE)?y(g({},e),{count:Math.max(e.count-1,0)}):t.type===s.di.REDIRECT?y(g({},e),{count:0}):e:e};let S=E;function k(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function x(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:O,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case s.di.MATCH_ROUTE:return M(x({},O),{currentUrl:t.pathname});case s.Ih.NOTIFY_SUCCESS:var n={component:t.component,props:t.props};return M(x({},e),{successes:[n],errors:[]});case s.Ih.NOTIFY_SUCCESS_MSG:return M(x({},e),{successes:[t.msg],errors:[]});case s.Ih.NOTIFY_ERROR:var r=t.error.errors,i=null==r?void 0:r.map(function(e){return L(t,e)});return M(x({},e),{successes:[],errors:i});case s.Ih.NOTIFY_ERROR_MSG:return M(x({},e),{successes:[],errors:[t.msg]});case s.Mk.RECEIVE_SIGNIN_FAIL:return M(x({},e),{successes:[],errors:["Your email or password is incorrect. Please try again"]});default:return e}};function L(e,t){return{component:e.component,props:{msg:t.detail}}}let C=A;function I(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function D(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:R,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case s.di.REDIRECT:return P(D({},e),{to:t.to});case s.di.MATCH_ROUTE:return P(D({},e),{to:void 0});default:return e}};let F=j;var Y=n(87013),B=(0,a.UY)({authentication:b,fetching:S,notifications:C,redirect:F,buildInfo:Y.Z});B(void 0,{type:"INITIAL_STATE"});var U=i.v9;let H=B},19084(e,t,n){"use strict";var r,i,a,o,s,u,c,l,f,d;n.d(t,{Ih:()=>i,Mk:()=>a,Y0:()=>s,di:()=>r,jp:()=>o}),n(67294),(u=r||(r={})).REDIRECT="REDIRECT",u.MATCH_ROUTE="MATCH_ROUTE",(c=i||(i={})).NOTIFY_SUCCESS="NOTIFY_SUCCESS",c.NOTIFY_SUCCESS_MSG="NOTIFY_SUCCESS_MSG",c.NOTIFY_ERROR="NOTIFY_ERROR",c.NOTIFY_ERROR_MSG="NOTIFY_ERROR_MSG",(l=a||(a={})).REQUEST_SIGNIN="REQUEST_SIGNIN",l.RECEIVE_SIGNIN_SUCCESS="RECEIVE_SIGNIN_SUCCESS",l.RECEIVE_SIGNIN_FAIL="RECEIVE_SIGNIN_FAIL",l.RECEIVE_SIGNIN_ERROR="RECEIVE_SIGNIN_ERROR",l.RECEIVE_SIGNOUT_SUCCESS="RECEIVE_SIGNOUT_SUCCESS",l.RECEIVE_SIGNOUT_ERROR="RECEIVE_SIGNOUT_ERROR",(f=o||(o={})).RECEIVE_CREATE_ERROR="RECEIVE_CREATE_ERROR",f.RECEIVE_CREATE_SUCCESS="RECEIVE_CREATE_SUCCESS",f.RECEIVE_DELETE_ERROR="RECEIVE_DELETE_ERROR",f.RECEIVE_DELETE_SUCCESS="RECEIVE_DELETE_SUCCESS",f.RECEIVE_UPDATE_ERROR="RECEIVE_UPDATE_ERROR",f.RECEIVE_UPDATE_SUCCESS="RECEIVE_UPDATE_SUCCESS",f.REQUEST_CREATE="REQUEST_CREATE",f.REQUEST_DELETE="REQUEST_DELETE",f.REQUEST_UPDATE="REQUEST_UPDATE",f.UPSERT_CONFIGURATION="UPSERT_CONFIGURATION",f.UPSERT_JOB_RUN="UPSERT_JOB_RUN",f.UPSERT_JOB_RUNS="UPSERT_JOB_RUNS",f.UPSERT_TRANSACTION="UPSERT_TRANSACTION",f.UPSERT_TRANSACTIONS="UPSERT_TRANSACTIONS",f.UPSERT_BUILD_INFO="UPSERT_BUILD_INFO",(d=s||(s={})).FETCH_BUILD_INFO_REQUESTED="FETCH_BUILD_INFO_REQUESTED",d.FETCH_BUILD_INFO_SUCCEEDED="FETCH_BUILD_INFO_SUCCEEDED",d.FETCH_BUILD_INFO_FAILED="FETCH_BUILD_INFO_FAILED"},87013(e,t,n){"use strict";n.d(t,{Y:()=>o,Z:()=>u});var r=n(19084);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:o,t=arguments.length>1?arguments[1]:void 0;return t.type===r.Y0.FETCH_BUILD_INFO_SUCCEEDED?a({},t.buildInfo):e};let u=s},34823(e,t,n){"use strict";n.d(t,{N:()=>r});var r=function(e){return e.buildInfo}},73343(e,t,n){"use strict";n.d(t,{r:()=>u});var r=n(19350),i=n(32316),a=n(59114),o=n(5324),s={props:{MuiGrid:{spacing:3*o.default.unit},MuiCardHeader:{titleTypographyProps:{color:"secondary"}}},palette:{action:{hoverOpacity:.3},primary:{light:"#E5F1FF",main:"#3c40c6",contrastText:"#fff"},secondary:{main:"#3d5170"},success:{light:"#e8faf1",main:r.ek.A700,dark:r.ek[700],contrastText:r.y0.white},warning:{light:"#FFFBF1",main:"#fff6b6",contrastText:"#fad27a"},error:{light:"#ffdada",main:"#f44336",dark:"#d32f2f",contrastText:"#fff"},background:{default:"#f5f6f8",appBar:"#3c40c6"},text:{primary:(0,a.darken)(r.BA.A700,.7),secondary:"#818ea3"},listPendingStatus:{background:"#fef7e5",color:"#fecb4c"},listCompletedStatus:{background:"#e9faf2",color:"#4ed495"}},shape:{borderRadius:o.default.unit},overrides:{MuiButton:{root:{borderRadius:o.default.unit/2,textTransform:"none"},sizeLarge:{padding:void 0,fontSize:void 0,paddingTop:o.default.unit,paddingBottom:o.default.unit,paddingLeft:5*o.default.unit,paddingRight:5*o.default.unit}},MuiTableCell:{body:{fontSize:"1rem"},head:{fontSize:"1rem",fontWeight:400}},MuiCardHeader:{root:{borderBottom:"1px solid rgba(0, 0, 0, 0.12)"},action:{marginTop:-2,marginRight:0,"& >*":{marginLeft:2*o.default.unit}},subheader:{marginTop:.5*o.default.unit}}},typography:{useNextVariants:!0,fontFamily:"-apple-system,BlinkMacSystemFont,Roboto,Helvetica,Arial,sans-serif",button:{textTransform:"none",fontSize:"1.2em"},body1:{fontSize:"1.0rem",fontWeight:400,lineHeight:"1.46429em",color:"rgba(0, 0, 0, 0.87)",letterSpacing:-.4},body2:{fontSize:"1.0rem",fontWeight:500,lineHeight:"1.71429em",color:"rgba(0, 0, 0, 0.87)",letterSpacing:-.4},body1Next:{color:"rgb(29, 29, 29)",fontWeight:400,fontSize:"1rem",lineHeight:1.5,letterSpacing:-.4},body2Next:{color:"rgb(29, 29, 29)",fontWeight:400,fontSize:"0.875rem",lineHeight:1.5,letterSpacing:-.4},display1:{color:"#818ea3",fontSize:"2.125rem",fontWeight:400,lineHeight:"1.20588em",letterSpacing:-.4},display2:{color:"#818ea3",fontSize:"2.8125rem",fontWeight:400,lineHeight:"1.13333em",marginLeft:"-.02em",letterSpacing:-.4},display3:{color:"#818ea3",fontSize:"3.5rem",fontWeight:400,lineHeight:"1.30357em",marginLeft:"-.02em",letterSpacing:-.4},display4:{fontSize:14,fontWeightLight:300,fontWeightMedium:500,fontWeightRegular:400,letterSpacing:-.4},h1:{color:"rgb(29, 29, 29)",fontSize:"6rem",fontWeight:300,lineHeight:1},h2:{color:"rgb(29, 29, 29)",fontSize:"3.75rem",fontWeight:300,lineHeight:1},h3:{color:"rgb(29, 29, 29)",fontSize:"3rem",fontWeight:400,lineHeight:1.04},h4:{color:"rgb(29, 29, 29)",fontSize:"2.125rem",fontWeight:400,lineHeight:1.17},h5:{color:"rgb(29, 29, 29)",fontSize:"1.5rem",fontWeight:400,lineHeight:1.33,letterSpacing:-.4},h6:{fontSize:"0.8rem",fontWeight:450,lineHeight:"1.71429em",color:"rgba(0, 0, 0, 0.87)",letterSpacing:-.4},subheading:{color:"rgb(29, 29, 29)",fontSize:"1rem",fontWeight:400,lineHeight:"1.5em",letterSpacing:-.4},subtitle1:{color:"rgb(29, 29, 29)",fontSize:"1rem",fontWeight:400,lineHeight:1.75,letterSpacing:-.4},subtitle2:{color:"rgb(29, 29, 29)",fontSize:"0.875rem",fontWeight:500,lineHeight:1.57,letterSpacing:-.4}},shadows:["none","0px 1px 3px 0px rgba(0, 0, 0, 0.1),0px 1px 1px 0px rgba(0, 0, 0, 0.04),0px 2px 1px -1px rgba(0, 0, 0, 0.02)","0px 1px 5px 0px rgba(0, 0, 0, 0.1),0px 2px 2px 0px rgba(0, 0, 0, 0.04),0px 3px 1px -2px rgba(0, 0, 0, 0.02)","0px 1px 8px 0px rgba(0, 0, 0, 0.1),0px 3px 4px 0px rgba(0, 0, 0, 0.04),0px 3px 3px -2px rgba(0, 0, 0, 0.02)","0px 2px 4px -1px rgba(0, 0, 0, 0.1),0px 4px 5px 0px rgba(0, 0, 0, 0.04),0px 1px 10px 0px rgba(0, 0, 0, 0.02)","0px 3px 5px -1px rgba(0, 0, 0, 0.1),0px 5px 8px 0px rgba(0, 0, 0, 0.04),0px 1px 14px 0px rgba(0, 0, 0, 0.02)","0px 3px 5px -1px rgba(0, 0, 0, 0.1),0px 6px 10px 0px rgba(0, 0, 0, 0.04),0px 1px 18px 0px rgba(0, 0, 0, 0.02)","0px 4px 5px -2px rgba(0, 0, 0, 0.1),0px 7px 10px 1px rgba(0, 0, 0, 0.04),0px 2px 16px 1px rgba(0, 0, 0, 0.02)","0px 5px 5px -3px rgba(0, 0, 0, 0.1),0px 8px 10px 1px rgba(0, 0, 0, 0.04),0px 3px 14px 2px rgba(0, 0, 0, 0.02)","0px 5px 6px -3px rgba(0, 0, 0, 0.1),0px 9px 12px 1px rgba(0, 0, 0, 0.04),0px 3px 16px 2px rgba(0, 0, 0, 0.02)","0px 6px 6px -3px rgba(0, 0, 0, 0.1),0px 10px 14px 1px rgba(0, 0, 0, 0.04),0px 4px 18px 3px rgba(0, 0, 0, 0.02)","0px 6px 7px -4px rgba(0, 0, 0, 0.1),0px 11px 15px 1px rgba(0, 0, 0, 0.04),0px 4px 20px 3px rgba(0, 0, 0, 0.02)","0px 7px 8px -4px rgba(0, 0, 0, 0.1),0px 12px 17px 2px rgba(0, 0, 0, 0.04),0px 5px 22px 4px rgba(0, 0, 0, 0.02)","0px 7px 8px -4px rgba(0, 0, 0, 0.1),0px 13px 19px 2px rgba(0, 0, 0, 0.04),0px 5px 24px 4px rgba(0, 0, 0, 0.02)","0px 7px 9px -4px rgba(0, 0, 0, 0.1),0px 14px 21px 2px rgba(0, 0, 0, 0.04),0px 5px 26px 4px rgba(0, 0, 0, 0.02)","0px 8px 9px -5px rgba(0, 0, 0, 0.1),0px 15px 22px 2px rgba(0, 0, 0, 0.04),0px 6px 28px 5px rgba(0, 0, 0, 0.02)","0px 8px 10px -5px rgba(0, 0, 0, 0.1),0px 16px 24px 2px rgba(0, 0, 0, 0.04),0px 6px 30px 5px rgba(0, 0, 0, 0.02)","0px 8px 11px -5px rgba(0, 0, 0, 0.1),0px 17px 26px 2px rgba(0, 0, 0, 0.04),0px 6px 32px 5px rgba(0, 0, 0, 0.02)","0px 9px 11px -5px rgba(0, 0, 0, 0.1),0px 18px 28px 2px rgba(0, 0, 0, 0.04),0px 7px 34px 6px rgba(0, 0, 0, 0.02)","0px 9px 12px -6px rgba(0, 0, 0, 0.1),0px 19px 29px 2px rgba(0, 0, 0, 0.04),0px 7px 36px 6px rgba(0, 0, 0, 0.02)","0px 10px 13px -6px rgba(0, 0, 0, 0.1),0px 20px 31px 3px rgba(0, 0, 0, 0.04),0px 8px 38px 7px rgba(0, 0, 0, 0.02)","0px 10px 13px -6px rgba(0, 0, 0, 0.1),0px 21px 33px 3px rgba(0, 0, 0, 0.04),0px 8px 40px 7px rgba(0, 0, 0, 0.02)","0px 10px 14px -6px rgba(0, 0, 0, 0.1),0px 22px 35px 3px rgba(0, 0, 0, 0.04),0px 8px 42px 7px rgba(0, 0, 0, 0.02)","0px 11px 14px -7px rgba(0, 0, 0, 0.1),0px 23px 36px 3px rgba(0, 0, 0, 0.04),0px 9px 44px 8px rgba(0, 0, 0, 0.02)","0px 11px 15px -7px rgba(0, 0, 0, 0.1),0px 24px 38px 3px rgba(0, 0, 0, 0.04),0px 9px 46px 8px rgba(0, 0, 0, 0.02)",]},u=(0,i.createMuiTheme)(s)},66289(e,t,n){"use strict";function r(e){if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function i(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}function a(){if("undefined"==typeof Reflect||!Reflect.construct||Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(e){return!1}}function o(e,t,n){return(o=a()?Reflect.construct:function(e,t,n){var r=[null];r.push.apply(r,t);var i=new(Function.bind.apply(e,r));return n&&f(i,n.prototype),i}).apply(null,arguments)}function s(e){return(s=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function u(e,t){if("function"!=typeof t&&null!==t)throw TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&f(e,t)}function c(e){return -1!==Function.toString.call(e).indexOf("[native code]")}function l(e,t){return t&&("object"===p(t)||"function"==typeof t)?t:r(e)}function f(e,t){return(f=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}n.d(t,{V0:()=>B,_7:()=>v});var d,h,p=function(e){return e&&"undefined"!=typeof Symbol&&e.constructor===Symbol?"symbol":typeof e};function b(e){var t="function"==typeof Map?new Map:void 0;return(b=function(e){if(null===e||!c(e))return e;if("function"!=typeof e)throw TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,n)}function n(){return o(e,arguments,s(this).constructor)}return n.prototype=Object.create(e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),f(n,e)})(e)}function m(){if("undefined"==typeof Reflect||!Reflect.construct||Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch(e){return!1}}function g(e){var t=m();return function(){var n,r=s(e);if(t){var i=s(this).constructor;n=Reflect.construct(r,arguments,i)}else n=r.apply(this,arguments);return l(this,n)}}var v=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"AuthenticationError(".concat(e.statusText,")"))).errors=[{status:e.status,detail:e},],r}return n}(b(Error)),y=function(e){u(n,e);var t=g(n);function n(e){var r,a=e.errors;return i(this,n),(r=t.call(this,"BadRequestError")).errors=a,r}return n}(b(Error)),w=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"UnprocessableEntityError")).errors=e,r}return n}(b(Error)),_=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"ServerError")).errors=e,r}return n}(b(Error)),E=function(e){u(n,e);var t=g(n);function n(e){var r,a=e.errors;return i(this,n),(r=t.call(this,"ConflictError")).errors=a,r}return n}(b(Error)),S=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"UnknownResponseError(".concat(e.statusText,")"))).errors=[{status:e.status,detail:e.statusText},],r}return n}(b(Error));function k(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:2e4;return Promise.race([fetch(e,t),new Promise(function(e,t){return setTimeout(function(){return t(Error("timeout"))},n)}),])}function x(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]=200&&e.status<300))return[3,2];return[2,e.json()];case 2:if(400!==e.status)return[3,3];return[2,e.json().then(function(e){throw new y(e)})];case 3:if(401!==e.status)return[3,4];throw new v(e);case 4:if(422!==e.status)return[3,6];return[4,$(e)];case 5:throw n=i.sent(),new w(n);case 6:if(409!==e.status)return[3,7];return[2,e.json().then(function(e){throw new E(e)})];case 7:if(!(e.status>=500))return[3,9];return[4,$(e)];case 8:throw r=i.sent(),new _(r);case 9:throw new S(e);case 10:return[2]}})})).apply(this,arguments)}function $(e){return z.apply(this,arguments)}function z(){return(z=j(function(e){return Y(this,function(t){return[2,e.json().then(function(t){return t.errors?t.errors.map(function(t){return{status:e.status,detail:t.detail}}):G(e)}).catch(function(){return G(e)})]})})).apply(this,arguments)}function G(e){return[{status:e.status,detail:e.statusText},]}},50109(e,t,n){"use strict";n.d(t,{LK:()=>o,U2:()=>i,eT:()=>s,t8:()=>a});var r=n(12795);function i(e){return r.ZP.getItem("chainlink.".concat(e))}function a(e,t){r.ZP.setItem("chainlink.".concat(e),t)}function o(e){var t=i(e),n={};if(t)try{return JSON.parse(t)}catch(r){}return n}function s(e,t){a(e,JSON.stringify(t))}},9541(e,t,n){"use strict";n.d(t,{Ks:()=>u,Tp:()=>a,iR:()=>o,pm:()=>s});var r=n(50109),i="persistURL";function a(){return r.U2(i)||""}function o(e){r.t8(i,e)}function s(){return r.LK("authentication")}function u(e){r.eT("authentication",e)}},67121(e,t,n){"use strict";function r(e){var t,n=e.Symbol;return"function"==typeof n?n.observable?t=n.observable:(t=n("observable"),n.observable=t):t="@@observable",t}n.r(t),n.d(t,{default:()=>o}),e=n.hmd(e),i="undefined"!=typeof self?self:"undefined"!=typeof window?window:void 0!==n.g?n.g:e;var i,a=r(i);let o=a},2177(e,t,n){"use strict";n.d(t,{Z:()=>o});var r=!0,i="Invariant failed";function a(e,t){if(!e){if(r)throw Error(i);throw Error(i+": "+(t||""))}}let o=a},11742(e){e.exports=function(){var e=document.getSelection();if(!e.rangeCount)return function(){};for(var t=document.activeElement,n=[],r=0;ru,ZT:()=>i,_T:()=>o,ev:()=>c,mG:()=>s,pi:()=>a});var r=function(e,t){return(r=Object.setPrototypeOf||({__proto__:[]})instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(e,t)};function i(e,t){if("function"!=typeof t&&null!==t)throw TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var a=function(){return(a=Object.assign||function(e){for(var t,n=1,r=arguments.length;nt.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n}function s(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||(n=Promise))(function(n,a){function o(e){try{u(r.next(e))}catch(t){a(t)}}function s(e){try{u(r.throw(e))}catch(t){a(t)}}function u(e){e.done?n(e.value):i(e.value).then(o,s)}u((r=r.apply(e,t||[])).next())})}function u(e,t){var n,r,i,a,o={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return a={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function s(e){return function(t){return u([e,t])}}function u(a){if(n)throw TypeError("Generator is already executing.");for(;o;)try{if(n=1,r&&(i=2&a[0]?r.return:a[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,a[1])).done)return i;switch(r=0,i&&(a=[2&a[0],i.value]),a[0]){case 0:case 1:i=a;break;case 4:return o.label++,{value:a[1],done:!1};case 5:o.label++,r=a[1],a=[0];continue;case 7:a=o.ops.pop(),o.trys.pop();continue;default:if(!(i=(i=o.trys).length>0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]r})},94927(e,t,n){function r(e,t){if(i("noDeprecation"))return e;var n=!1;function r(){if(!n){if(i("throwDeprecation"))throw Error(t);i("traceDeprecation")?console.trace(t):console.warn(t),n=!0}return e.apply(this,arguments)}return r}function i(e){try{if(!n.g.localStorage)return!1}catch(t){return!1}var r=n.g.localStorage[e];return null!=r&&"true"===String(r).toLowerCase()}e.exports=r},42473(e){"use strict";var t=function(){};e.exports=t},84763(e){e.exports=Worker},47529(e){e.exports=n;var t=Object.prototype.hasOwnProperty;function n(){for(var e={},n=0;ne.length)&&(t=e.length);for(var n=0,r=Array(t);n=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}e.exports=i,e.exports.__esModule=!0,e.exports.default=e.exports},7071(e){function t(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},94993(e,t,n){var r=n(18698).default,i=n(66115);function a(e,t){if(t&&("object"===r(t)||"function"==typeof t))return t;if(void 0!==t)throw TypeError("Derived constructors may only return object or undefined");return i(e)}e.exports=a,e.exports.__esModule=!0,e.exports.default=e.exports},6015(e){function t(n,r){return e.exports=t=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},e.exports.__esModule=!0,e.exports.default=e.exports,t(n,r)}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},861(e,t,n){var r=n(63405),i=n(79498),a=n(86116),o=n(42281);function s(e){return r(e)||i(e)||a(e)||o()}e.exports=s,e.exports.__esModule=!0,e.exports.default=e.exports},18698(e){function t(n){return e.exports=t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e.exports.__esModule=!0,e.exports.default=e.exports,t(n)}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},86116(e,t,n){var r=n(73897);function i(e,t){if(e){if("string"==typeof e)return r(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);if("Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return r(e,t)}}e.exports=i,e.exports.__esModule=!0,e.exports.default=e.exports},1644(e,t,n){"use strict";var r,i;function a(e){return!!e&&e<7}n.d(t,{I:()=>r,O:()=>a}),(i=r||(r={}))[i.loading=1]="loading",i[i.setVariables=2]="setVariables",i[i.fetchMore=3]="fetchMore",i[i.refetch=4]="refetch",i[i.poll=6]="poll",i[i.ready=7]="ready",i[i.error=8]="error"},30990(e,t,n){"use strict";n.d(t,{MS:()=>s,YG:()=>a,cA:()=>c,ls:()=>o});var r=n(70655);n(83952);var i=n(13154),a=Symbol();function o(e){return!!e.extensions&&Array.isArray(e.extensions[a])}function s(e){return e.hasOwnProperty("graphQLErrors")}var u=function(e){var t=(0,r.ev)((0,r.ev)((0,r.ev)([],e.graphQLErrors,!0),e.clientErrors,!0),e.protocolErrors,!0);return e.networkError&&t.push(e.networkError),t.map(function(e){return(0,i.s)(e)&&e.message||"Error message not found."}).join("\n")},c=function(e){function t(n){var r=n.graphQLErrors,i=n.protocolErrors,a=n.clientErrors,o=n.networkError,s=n.errorMessage,c=n.extraInfo,l=e.call(this,s)||this;return l.name="ApolloError",l.graphQLErrors=r||[],l.protocolErrors=i||[],l.clientErrors=a||[],l.networkError=o||null,l.message=s||u(l),l.extraInfo=c,l.__proto__=t.prototype,l}return(0,r.ZT)(t,e),t}(Error)},85317(e,t,n){"use strict";n.d(t,{K:()=>a});var r=n(67294),i=n(30320).aS?Symbol.for("__APOLLO_CONTEXT__"):"__APOLLO_CONTEXT__";function a(){var e=r.createContext[i];return e||(Object.defineProperty(r.createContext,i,{value:e=r.createContext({}),enumerable:!1,writable:!1,configurable:!0}),e.displayName="ApolloContext"),e}},21436(e,t,n){"use strict";n.d(t,{O:()=>i,k:()=>r});var r=Array.isArray;function i(e){return Array.isArray(e)&&e.length>0}},30320(e,t,n){"use strict";n.d(t,{DN:()=>s,JC:()=>l,aS:()=>o,mr:()=>i,sy:()=>a});var r=n(83952),i="function"==typeof WeakMap&&"ReactNative"!==(0,r.wY)(function(){return navigator.product}),a="function"==typeof WeakSet,o="function"==typeof Symbol&&"function"==typeof Symbol.for,s=o&&Symbol.asyncIterator,u="function"==typeof(0,r.wY)(function(){return window.document.createElement}),c=(0,r.wY)(function(){return navigator.userAgent.indexOf("jsdom")>=0})||!1,l=u&&!c},53712(e,t,n){"use strict";function r(){for(var e=[],t=0;tr})},10542(e,t,n){"use strict";n.d(t,{J:()=>o}),n(83952);var r=n(13154);function i(e){var t=new Set([e]);return t.forEach(function(e){(0,r.s)(e)&&a(e)===e&&Object.getOwnPropertyNames(e).forEach(function(n){(0,r.s)(e[n])&&t.add(e[n])})}),e}function a(e){if(__DEV__&&!Object.isFrozen(e))try{Object.freeze(e)}catch(t){if(t instanceof TypeError)return null;throw t}return e}function o(e){return __DEV__&&i(e),e}},14012(e,t,n){"use strict";n.d(t,{J:()=>a});var r=n(70655),i=n(53712);function a(e,t){return(0,i.o)(e,t,t.variables&&{variables:(0,r.pi)((0,r.pi)({},e&&e.variables),t.variables)})}},13154(e,t,n){"use strict";function r(e){return null!==e&&"object"==typeof e}n.d(t,{s:()=>r})},83952(e,t,n){"use strict";n.d(t,{ej:()=>u,kG:()=>c,wY:()=>h});var r,i=n(70655),a="Invariant Violation",o=Object.setPrototypeOf,s=void 0===o?function(e,t){return e.__proto__=t,e}:o,u=function(e){function t(n){void 0===n&&(n=a);var r=e.call(this,"number"==typeof n?a+": "+n+" (see https://github.com/apollographql/invariant-packages)":n)||this;return r.framesToPop=1,r.name=a,s(r,t.prototype),r}return(0,i.ZT)(t,e),t}(Error);function c(e,t){if(!e)throw new u(t)}var l=["debug","log","warn","error","silent"],f=l.indexOf("log");function d(e){return function(){if(l.indexOf(e)>=f)return(console[e]||console.log).apply(console,arguments)}}function h(e){try{return e()}catch(t){}}(r=c||(c={})).debug=d("debug"),r.log=d("log"),r.warn=d("warn"),r.error=d("error");let p=h(function(){return globalThis})||h(function(){return window})||h(function(){return self})||h(function(){return global})||h(function(){return h.constructor("return this")()});var b="__",m=[b,b].join("DEV");function g(){try{return Boolean(__DEV__)}catch(e){return Object.defineProperty(p,m,{value:"production"!==h(function(){return"production"}),enumerable:!1,configurable:!0,writable:!0}),p[m]}}let v=g();function y(e){try{return e()}catch(t){}}var w=y(function(){return globalThis})||y(function(){return window})||y(function(){return self})||y(function(){return global})||y(function(){return y.constructor("return this")()}),_=!1;function E(){!w||y(function(){return"production"})||y(function(){return process})||(Object.defineProperty(w,"process",{value:{env:{NODE_ENV:"production"}},configurable:!0,enumerable:!1,writable:!0}),_=!0)}function S(){_&&(delete w.process,_=!1)}E();var k=n(10143);function x(){return k.H,S()}function T(){__DEV__?c("boolean"==typeof v,v):c("boolean"==typeof v,39)}x(),T()},4942(e,t,n){"use strict";function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}n.d(t,{Z:()=>r})},87462(e,t,n){"use strict";function r(){return(r=Object.assign?Object.assign.bind():function(e){for(var t=1;tr})},51721(e,t,n){"use strict";function r(e,t){return(r=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e})(e,t)}function i(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,r(e,t)}n.d(t,{Z:()=>i})},63366(e,t,n){"use strict";function r(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}n.d(t,{Z:()=>r})},25821(e,t,n){"use strict";n.d(t,{Z:()=>s});var r=n(45695);function i(e){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var a=10,o=2;function s(e){return u(e,[])}function u(e,t){switch(i(e)){case"string":return JSON.stringify(e);case"function":return e.name?"[function ".concat(e.name,"]"):"[function]";case"object":if(null===e)return"null";return c(e,t);default:return String(e)}}function c(e,t){if(-1!==t.indexOf(e))return"[Circular]";var n=[].concat(t,[e]),r=d(e);if(void 0!==r){var i=r.call(e);if(i!==e)return"string"==typeof i?i:u(i,n)}else if(Array.isArray(e))return f(e,n);return l(e,n)}function l(e,t){var n=Object.keys(e);return 0===n.length?"{}":t.length>o?"["+h(e)+"]":"{ "+n.map(function(n){var r=u(e[n],t);return n+": "+r}).join(", ")+" }"}function f(e,t){if(0===e.length)return"[]";if(t.length>o)return"[Array]";for(var n=Math.min(a,e.length),r=e.length-n,i=[],s=0;s1&&i.push("... ".concat(r," more items")),"["+i.join(", ")+"]"}function d(e){var t=e[String(r.Z)];return"function"==typeof t?t:"function"==typeof e.inspect?e.inspect:void 0}function h(e){var t=Object.prototype.toString.call(e).replace(/^\[object /,"").replace(/]$/,"");if("Object"===t&&"function"==typeof e.constructor){var n=e.constructor.name;if("string"==typeof n&&""!==n)return n}return t}},45695(e,t,n){"use strict";n.d(t,{Z:()=>i});var r="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):void 0;let i=r},25217(e,t,n){"use strict";function r(e,t){if(!Boolean(e))throw Error(null!=t?t:"Unexpected invariant triggered.")}n.d(t,{Ye:()=>o,WU:()=>s,UG:()=>u});var i=n(45695);function a(e){var t=e.prototype.toJSON;"function"==typeof t||r(0),e.prototype.inspect=t,i.Z&&(e.prototype[i.Z]=t)}var o=function(){function e(e,t,n){this.start=e.start,this.end=t.end,this.startToken=e,this.endToken=t,this.source=n}return e.prototype.toJSON=function(){return{start:this.start,end:this.end}},e}();a(o);var s=function(){function e(e,t,n,r,i,a,o){this.kind=e,this.start=t,this.end=n,this.line=r,this.column=i,this.value=o,this.prev=a,this.next=null}return e.prototype.toJSON=function(){return{kind:this.kind,value:this.value,line:this.line,column:this.column}},e}();function u(e){return null!=e&&"string"==typeof e.kind}a(s)},87392(e,t,n){"use strict";function r(e){var t=e.split(/\r\n|[\n\r]/g),n=a(e);if(0!==n)for(var r=1;ro&&i(t[s-1]);)--s;return t.slice(o,s).join("\n")}function i(e){for(var t=0;t1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],r=-1===e.indexOf("\n"),i=" "===e[0]||" "===e[0],a='"'===e[e.length-1],o="\\"===e[e.length-1],s=!r||a||o||n,u="";return s&&!(r&&i)&&(u+="\n"+t),u+=t?e.replace(/\n/g,"\n"+t):e,s&&(u+="\n"),'"""'+u.replace(/"""/g,'\\"""')+'"""'}n.d(t,{LZ:()=>o,W7:()=>r})},97359(e,t,n){"use strict";n.d(t,{h:()=>r});var r=Object.freeze({NAME:"Name",DOCUMENT:"Document",OPERATION_DEFINITION:"OperationDefinition",VARIABLE_DEFINITION:"VariableDefinition",SELECTION_SET:"SelectionSet",FIELD:"Field",ARGUMENT:"Argument",FRAGMENT_SPREAD:"FragmentSpread",INLINE_FRAGMENT:"InlineFragment",FRAGMENT_DEFINITION:"FragmentDefinition",VARIABLE:"Variable",INT:"IntValue",FLOAT:"FloatValue",STRING:"StringValue",BOOLEAN:"BooleanValue",NULL:"NullValue",ENUM:"EnumValue",LIST:"ListValue",OBJECT:"ObjectValue",OBJECT_FIELD:"ObjectField",DIRECTIVE:"Directive",NAMED_TYPE:"NamedType",LIST_TYPE:"ListType",NON_NULL_TYPE:"NonNullType",SCHEMA_DEFINITION:"SchemaDefinition",OPERATION_TYPE_DEFINITION:"OperationTypeDefinition",SCALAR_TYPE_DEFINITION:"ScalarTypeDefinition",OBJECT_TYPE_DEFINITION:"ObjectTypeDefinition",FIELD_DEFINITION:"FieldDefinition",INPUT_VALUE_DEFINITION:"InputValueDefinition",INTERFACE_TYPE_DEFINITION:"InterfaceTypeDefinition",UNION_TYPE_DEFINITION:"UnionTypeDefinition",ENUM_TYPE_DEFINITION:"EnumTypeDefinition",ENUM_VALUE_DEFINITION:"EnumValueDefinition",INPUT_OBJECT_TYPE_DEFINITION:"InputObjectTypeDefinition",DIRECTIVE_DEFINITION:"DirectiveDefinition",SCHEMA_EXTENSION:"SchemaExtension",SCALAR_TYPE_EXTENSION:"ScalarTypeExtension",OBJECT_TYPE_EXTENSION:"ObjectTypeExtension",INTERFACE_TYPE_EXTENSION:"InterfaceTypeExtension",UNION_TYPE_EXTENSION:"UnionTypeExtension",ENUM_TYPE_EXTENSION:"EnumTypeExtension",INPUT_OBJECT_TYPE_EXTENSION:"InputObjectTypeExtension"})},10143(e,t,n){"use strict";n.d(t,{H:()=>c,T:()=>l});var r=n(99763),i=n(25821);function a(e,t){if(!Boolean(e))throw Error(t)}let o=function(e,t){return e instanceof t};function s(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:"GraphQL request",n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{line:1,column:1};"string"==typeof e||a(0,"Body must be a string. Received: ".concat((0,i.Z)(e),".")),this.body=e,this.name=t,this.locationOffset=n,this.locationOffset.line>0||a(0,"line in locationOffset is 1-indexed and must be positive."),this.locationOffset.column>0||a(0,"column in locationOffset is 1-indexed and must be positive.")}return u(e,[{key:r.YF,get:function(){return"Source"}}]),e}();function l(e){return o(e,c)}},99763(e,t,n){"use strict";n.d(t,{YF:()=>r});var r="function"==typeof Symbol&&null!=Symbol.toStringTag?Symbol.toStringTag:"@@toStringTag"},37452(e){"use strict";e.exports=JSON.parse('{"AElig":"\xc6","AMP":"&","Aacute":"\xc1","Acirc":"\xc2","Agrave":"\xc0","Aring":"\xc5","Atilde":"\xc3","Auml":"\xc4","COPY":"\xa9","Ccedil":"\xc7","ETH":"\xd0","Eacute":"\xc9","Ecirc":"\xca","Egrave":"\xc8","Euml":"\xcb","GT":">","Iacute":"\xcd","Icirc":"\xce","Igrave":"\xcc","Iuml":"\xcf","LT":"<","Ntilde":"\xd1","Oacute":"\xd3","Ocirc":"\xd4","Ograve":"\xd2","Oslash":"\xd8","Otilde":"\xd5","Ouml":"\xd6","QUOT":"\\"","REG":"\xae","THORN":"\xde","Uacute":"\xda","Ucirc":"\xdb","Ugrave":"\xd9","Uuml":"\xdc","Yacute":"\xdd","aacute":"\xe1","acirc":"\xe2","acute":"\xb4","aelig":"\xe6","agrave":"\xe0","amp":"&","aring":"\xe5","atilde":"\xe3","auml":"\xe4","brvbar":"\xa6","ccedil":"\xe7","cedil":"\xb8","cent":"\xa2","copy":"\xa9","curren":"\xa4","deg":"\xb0","divide":"\xf7","eacute":"\xe9","ecirc":"\xea","egrave":"\xe8","eth":"\xf0","euml":"\xeb","frac12":"\xbd","frac14":"\xbc","frac34":"\xbe","gt":">","iacute":"\xed","icirc":"\xee","iexcl":"\xa1","igrave":"\xec","iquest":"\xbf","iuml":"\xef","laquo":"\xab","lt":"<","macr":"\xaf","micro":"\xb5","middot":"\xb7","nbsp":"\xa0","not":"\xac","ntilde":"\xf1","oacute":"\xf3","ocirc":"\xf4","ograve":"\xf2","ordf":"\xaa","ordm":"\xba","oslash":"\xf8","otilde":"\xf5","ouml":"\xf6","para":"\xb6","plusmn":"\xb1","pound":"\xa3","quot":"\\"","raquo":"\xbb","reg":"\xae","sect":"\xa7","shy":"\xad","sup1":"\xb9","sup2":"\xb2","sup3":"\xb3","szlig":"\xdf","thorn":"\xfe","times":"\xd7","uacute":"\xfa","ucirc":"\xfb","ugrave":"\xf9","uml":"\xa8","uuml":"\xfc","yacute":"\xfd","yen":"\xa5","yuml":"\xff"}')},93580(e){"use strict";e.exports=JSON.parse('{"0":"�","128":"€","130":"‚","131":"ƒ","132":"„","133":"…","134":"†","135":"‡","136":"ˆ","137":"‰","138":"Š","139":"‹","140":"Œ","142":"Ž","145":"‘","146":"’","147":"“","148":"”","149":"•","150":"–","151":"—","152":"˜","153":"™","154":"š","155":"›","156":"œ","158":"ž","159":"Ÿ"}')},67946(e){"use strict";e.exports=JSON.parse('{"locale":"en","long":{"year":{"previous":"last year","current":"this year","next":"next year","past":{"one":"{0} year ago","other":"{0} years ago"},"future":{"one":"in {0} year","other":"in {0} years"}},"quarter":{"previous":"last quarter","current":"this quarter","next":"next quarter","past":{"one":"{0} quarter ago","other":"{0} quarters ago"},"future":{"one":"in {0} quarter","other":"in {0} quarters"}},"month":{"previous":"last month","current":"this month","next":"next month","past":{"one":"{0} month ago","other":"{0} months ago"},"future":{"one":"in {0} month","other":"in {0} months"}},"week":{"previous":"last week","current":"this week","next":"next week","past":{"one":"{0} week ago","other":"{0} weeks ago"},"future":{"one":"in {0} week","other":"in {0} weeks"}},"day":{"previous":"yesterday","current":"today","next":"tomorrow","past":{"one":"{0} day ago","other":"{0} days ago"},"future":{"one":"in {0} day","other":"in {0} days"}},"hour":{"current":"this hour","past":{"one":"{0} hour ago","other":"{0} hours ago"},"future":{"one":"in {0} hour","other":"in {0} hours"}},"minute":{"current":"this minute","past":{"one":"{0} minute ago","other":"{0} minutes ago"},"future":{"one":"in {0} minute","other":"in {0} minutes"}},"second":{"current":"now","past":{"one":"{0} second ago","other":"{0} seconds ago"},"future":{"one":"in {0} second","other":"in {0} seconds"}}},"short":{"year":{"previous":"last yr.","current":"this yr.","next":"next yr.","past":"{0} yr. ago","future":"in {0} yr."},"quarter":{"previous":"last qtr.","current":"this qtr.","next":"next qtr.","past":{"one":"{0} qtr. ago","other":"{0} qtrs. ago"},"future":{"one":"in {0} qtr.","other":"in {0} qtrs."}},"month":{"previous":"last mo.","current":"this mo.","next":"next mo.","past":"{0} mo. ago","future":"in {0} mo."},"week":{"previous":"last wk.","current":"this wk.","next":"next wk.","past":"{0} wk. ago","future":"in {0} wk."},"day":{"previous":"yesterday","current":"today","next":"tomorrow","past":{"one":"{0} day ago","other":"{0} days ago"},"future":{"one":"in {0} day","other":"in {0} days"}},"hour":{"current":"this hour","past":"{0} hr. ago","future":"in {0} hr."},"minute":{"current":"this minute","past":"{0} min. ago","future":"in {0} min."},"second":{"current":"now","past":"{0} sec. ago","future":"in {0} sec."}},"narrow":{"year":{"previous":"last yr.","current":"this yr.","next":"next yr.","past":"{0} yr. ago","future":"in {0} yr."},"quarter":{"previous":"last qtr.","current":"this qtr.","next":"next qtr.","past":{"one":"{0} qtr. ago","other":"{0} qtrs. ago"},"future":{"one":"in {0} qtr.","other":"in {0} qtrs."}},"month":{"previous":"last mo.","current":"this mo.","next":"next mo.","past":"{0} mo. ago","future":"in {0} mo."},"week":{"previous":"last wk.","current":"this wk.","next":"next wk.","past":"{0} wk. ago","future":"in {0} wk."},"day":{"previous":"yesterday","current":"today","next":"tomorrow","past":{"one":"{0} day ago","other":"{0} days ago"},"future":{"one":"in {0} day","other":"in {0} days"}},"hour":{"current":"this hour","past":"{0} hr. ago","future":"in {0} hr."},"minute":{"current":"this minute","past":"{0} min. ago","future":"in {0} min."},"second":{"current":"now","past":"{0} sec. ago","future":"in {0} sec."}},"now":{"now":{"current":"now","future":"in a moment","past":"just now"}},"mini":{"year":"{0}yr","month":"{0}mo","week":"{0}wk","day":"{0}d","hour":"{0}h","minute":"{0}m","second":"{0}s","now":"now"},"short-time":{"year":"{0} yr.","month":"{0} mo.","week":"{0} wk.","day":{"one":"{0} day","other":"{0} days"},"hour":"{0} hr.","minute":"{0} min.","second":"{0} sec."},"long-time":{"year":{"one":"{0} year","other":"{0} years"},"month":{"one":"{0} month","other":"{0} months"},"week":{"one":"{0} week","other":"{0} weeks"},"day":{"one":"{0} day","other":"{0} days"},"hour":{"one":"{0} hour","other":"{0} hours"},"minute":{"one":"{0} minute","other":"{0} minutes"},"second":{"one":"{0} second","other":"{0} seconds"}}}')}},__webpack_module_cache__={};function __webpack_require__(e){var t=__webpack_module_cache__[e];if(void 0!==t)return t.exports;var n=__webpack_module_cache__[e]={id:e,loaded:!1,exports:{}};return __webpack_modules__[e].call(n.exports,n,n.exports,__webpack_require__),n.loaded=!0,n.exports}__webpack_require__.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return __webpack_require__.d(t,{a:t}),t},(()=>{var e,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__;__webpack_require__.t=function(n,r){if(1&r&&(n=this(n)),8&r||"object"==typeof n&&n&&(4&r&&n.__esModule||16&r&&"function"==typeof n.then))return n;var i=Object.create(null);__webpack_require__.r(i);var a={};e=e||[null,t({}),t([]),t(t)];for(var o=2&r&&n;"object"==typeof o&&!~e.indexOf(o);o=t(o))Object.getOwnPropertyNames(o).forEach(e=>a[e]=()=>n[e]);return a.default=()=>n,__webpack_require__.d(i,a),i}})(),__webpack_require__.d=(e,t)=>{for(var n in t)__webpack_require__.o(t,n)&&!__webpack_require__.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},__webpack_require__.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),__webpack_require__.hmd=e=>((e=Object.create(e)).children||(e.children=[]),Object.defineProperty(e,"exports",{enumerable:!0,set(){throw Error("ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: "+e.id)}}),e),__webpack_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),__webpack_require__.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},__webpack_require__.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),__webpack_require__.p="/assets/",__webpack_require__.nc=void 0;var __webpack_exports__={};(()=>{"use strict";var e,t,n,r,i=__webpack_require__(32316),a=__webpack_require__(8126),o=__webpack_require__(5690),s=__webpack_require__(30381),u=__webpack_require__.n(s),c=__webpack_require__(67294),l=__webpack_require__(73935),f=__webpack_require__.n(l),d=__webpack_require__(57209),h=__webpack_require__(37703),p=__webpack_require__(97779),b=__webpack_require__(28500);function m(e){return function(t){var n=t.dispatch,r=t.getState;return function(t){return function(i){return"function"==typeof i?i(n,r,e):t(i)}}}}var g=m();g.withExtraArgument=m;let v=g;var y=__webpack_require__(76489);function w(e){return function(t){return function(n){return function(r){n(r);var i=e||document&&document.cookie||"",a=t.getState();if("MATCH_ROUTE"===r.type&&"/signin"!==a.notifications.currentUrl){var o=(0,y.Q)(i);if(o.explorer)try{var s=JSON.parse(o.explorer);if("error"===s.status){var u=_(s.url);n({type:"NOTIFY_ERROR_MSG",msg:u})}}catch(c){n({type:"NOTIFY_ERROR_MSG",msg:"Invalid explorer status"})}}}}}}function _(e){var t="Can't connect to explorer: ".concat(e);return e.match(/^wss?:.+/)?t:"".concat(t,". You must use a websocket.")}var E=__webpack_require__(16353);function S(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}}}throw TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function ei(e,t){if(e){if("string"==typeof e)return ea(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);if("Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return ea(e,t)}}function ea(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n1,i=!1,a=arguments[1],o=a;return new n(function(n){return t.subscribe({next:function(t){var a=!i;if(i=!0,!a||r)try{o=e(o,t)}catch(s){return n.error(s)}else o=t},error:function(e){n.error(e)},complete:function(){if(!i&&!r)return n.error(TypeError("Cannot reduce an empty sequence"));n.next(o),n.complete()}})})},t.concat=function(){for(var e=this,t=arguments.length,n=Array(t),r=0;r=0&&i.splice(e,1),o()}});i.push(s)},error:function(e){r.error(e)},complete:function(){o()}});function o(){a.closed&&0===i.length&&r.complete()}return function(){i.forEach(function(e){return e.unsubscribe()}),a.unsubscribe()}})},t[ed]=function(){return this},e.from=function(t){var n="function"==typeof this?this:e;if(null==t)throw TypeError(t+" is not an object");var r=ep(t,ed);if(r){var i=r.call(t);if(Object(i)!==i)throw TypeError(i+" is not an object");return em(i)&&i.constructor===n?i:new n(function(e){return i.subscribe(e)})}if(ec("iterator")&&(r=ep(t,ef)))return new n(function(e){ev(function(){if(!e.closed){for(var n,i=er(r.call(t));!(n=i()).done;){var a=n.value;if(e.next(a),e.closed)return}e.complete()}})});if(Array.isArray(t))return new n(function(e){ev(function(){if(!e.closed){for(var n=0;n0))return n.connection.key;var r=n.connection.filter?n.connection.filter:[];r.sort();var i={};return r.forEach(function(e){i[e]=t[e]}),"".concat(n.connection.key,"(").concat(eV(i),")")}var a=e;if(t){var o=eV(t);a+="(".concat(o,")")}return n&&Object.keys(n).forEach(function(e){-1===eW.indexOf(e)&&(n[e]&&Object.keys(n[e]).length?a+="@".concat(e,"(").concat(eV(n[e]),")"):a+="@".concat(e))}),a},{setStringify:function(e){var t=eV;return eV=e,t}}),eV=function(e){return JSON.stringify(e,eq)};function eq(e,t){return(0,eO.s)(t)&&!Array.isArray(t)&&(t=Object.keys(t).sort().reduce(function(e,n){return e[n]=t[n],e},{})),t}function eZ(e,t){if(e.arguments&&e.arguments.length){var n={};return e.arguments.forEach(function(e){var r;return ez(n,e.name,e.value,t)}),n}return null}function eX(e){return e.alias?e.alias.value:e.name.value}function eJ(e,t,n){for(var r,i=0,a=t.selections;it.indexOf(i))throw __DEV__?new Q.ej("illegal argument: ".concat(i)):new Q.ej(27)}return e}function tt(e,t){return t?t(e):eT.of()}function tn(e){return"function"==typeof e?new ta(e):e}function tr(e){return e.request.length<=1}var ti=function(e){function t(t,n){var r=e.call(this,t)||this;return r.link=n,r}return(0,en.ZT)(t,e),t}(Error),ta=function(){function e(e){e&&(this.request=e)}return e.empty=function(){return new e(function(){return eT.of()})},e.from=function(t){return 0===t.length?e.empty():t.map(tn).reduce(function(e,t){return e.concat(t)})},e.split=function(t,n,r){var i=tn(n),a=tn(r||new e(tt));return new e(tr(i)&&tr(a)?function(e){return t(e)?i.request(e)||eT.of():a.request(e)||eT.of()}:function(e,n){return t(e)?i.request(e,n)||eT.of():a.request(e,n)||eT.of()})},e.execute=function(e,t){return e.request(eM(t.context,e7(te(t))))||eT.of()},e.concat=function(t,n){var r=tn(t);if(tr(r))return __DEV__&&Q.kG.warn(new ti("You are calling concat on a terminating link, which will have no effect",r)),r;var i=tn(n);return new e(tr(i)?function(e){return r.request(e,function(e){return i.request(e)||eT.of()})||eT.of()}:function(e,t){return r.request(e,function(e){return i.request(e,t)||eT.of()})||eT.of()})},e.prototype.split=function(t,n,r){return this.concat(e.split(t,n,r||new e(tt)))},e.prototype.concat=function(t){return e.concat(this,t)},e.prototype.request=function(e,t){throw __DEV__?new Q.ej("request is not implemented"):new Q.ej(22)},e.prototype.onError=function(e,t){if(t&&t.error)return t.error(e),!1;throw e},e.prototype.setOnError=function(e){return this.onError=e,this},e}(),to=__webpack_require__(25821),ts=__webpack_require__(25217),tu={Name:[],Document:["definitions"],OperationDefinition:["name","variableDefinitions","directives","selectionSet"],VariableDefinition:["variable","type","defaultValue","directives"],Variable:["name"],SelectionSet:["selections"],Field:["alias","name","arguments","directives","selectionSet"],Argument:["name","value"],FragmentSpread:["name","directives"],InlineFragment:["typeCondition","directives","selectionSet"],FragmentDefinition:["name","variableDefinitions","typeCondition","directives","selectionSet"],IntValue:[],FloatValue:[],StringValue:[],BooleanValue:[],NullValue:[],EnumValue:[],ListValue:["values"],ObjectValue:["fields"],ObjectField:["name","value"],Directive:["name","arguments"],NamedType:["name"],ListType:["type"],NonNullType:["type"],SchemaDefinition:["description","directives","operationTypes"],OperationTypeDefinition:["type"],ScalarTypeDefinition:["description","name","directives"],ObjectTypeDefinition:["description","name","interfaces","directives","fields"],FieldDefinition:["description","name","arguments","type","directives"],InputValueDefinition:["description","name","type","defaultValue","directives"],InterfaceTypeDefinition:["description","name","interfaces","directives","fields"],UnionTypeDefinition:["description","name","directives","types"],EnumTypeDefinition:["description","name","directives","values"],EnumValueDefinition:["description","name","directives"],InputObjectTypeDefinition:["description","name","directives","fields"],DirectiveDefinition:["description","name","arguments","locations"],SchemaExtension:["directives","operationTypes"],ScalarTypeExtension:["name","directives"],ObjectTypeExtension:["name","interfaces","directives","fields"],InterfaceTypeExtension:["name","interfaces","directives","fields"],UnionTypeExtension:["name","directives","types"],EnumTypeExtension:["name","directives","values"],InputObjectTypeExtension:["name","directives","fields"]},tc=Object.freeze({});function tl(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:tu,r=void 0,i=Array.isArray(e),a=[e],o=-1,s=[],u=void 0,c=void 0,l=void 0,f=[],d=[],h=e;do{var p,b=++o===a.length,m=b&&0!==s.length;if(b){if(c=0===d.length?void 0:f[f.length-1],u=l,l=d.pop(),m){if(i)u=u.slice();else{for(var g={},v=0,y=Object.keys(u);v1)for(var r=new tB,i=1;i=0;--a){var o=i[a],s=isNaN(+o)?{}:[];s[o]=t,t=s}n=r.merge(n,t)}),n}var tW=Object.prototype.hasOwnProperty;function tK(e,t){var n,r,i,a,o;return(0,en.mG)(this,void 0,void 0,function(){var s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T,M,O,A;return(0,en.Jh)(this,function(L){switch(L.label){case 0:if(void 0===TextDecoder)throw Error("TextDecoder must be defined in the environment: please import a polyfill.");s=new TextDecoder("utf-8"),u=null===(n=e.headers)||void 0===n?void 0:n.get("content-type"),c="boundary=",l=(null==u?void 0:u.includes(c))?null==u?void 0:u.substring((null==u?void 0:u.indexOf(c))+c.length).replace(/['"]/g,"").replace(/\;(.*)/gm,"").trim():"-",f="\r\n--".concat(l),d="",h=tI(e),p=!0,L.label=1;case 1:if(!p)return[3,3];return[4,h.next()];case 2:for(m=(b=L.sent()).value,g=b.done,v="string"==typeof m?m:s.decode(m),y=d.length-f.length+1,p=!g,d+=v,w=d.indexOf(f,y);w>-1;){if(_=void 0,_=(O=[d.slice(0,w),d.slice(w+f.length),])[0],d=O[1],E=_.indexOf("\r\n\r\n"),(k=(S=tV(_.slice(0,E)))["content-type"])&&-1===k.toLowerCase().indexOf("application/json"))throw Error("Unsupported patch content type: application/json is required.");if(x=_.slice(E))try{T=tq(e,x),Object.keys(T).length>1||"data"in T||"incremental"in T||"errors"in T||"payload"in T?tz(T)?(M={},"payload"in T&&(M=(0,en.pi)({},T.payload)),"errors"in T&&(M=(0,en.pi)((0,en.pi)({},M),{extensions:(0,en.pi)((0,en.pi)({},"extensions"in M?M.extensions:null),((A={})[tN.YG]=T.errors,A))})),null===(r=t.next)||void 0===r||r.call(t,M)):null===(i=t.next)||void 0===i||i.call(t,T):1===Object.keys(T).length&&"hasNext"in T&&!T.hasNext&&(null===(a=t.complete)||void 0===a||a.call(t))}catch(C){tZ(C,t)}w=d.indexOf(f)}return[3,1];case 3:return null===(o=t.complete)||void 0===o||o.call(t),[2]}})})}function tV(e){var t={};return e.split("\n").forEach(function(e){var n=e.indexOf(":");if(n>-1){var r=e.slice(0,n).trim().toLowerCase(),i=e.slice(n+1).trim();t[r]=i}}),t}function tq(e,t){e.status>=300&&tD(e,function(){try{return JSON.parse(t)}catch(e){return t}}(),"Response not successful: Received status code ".concat(e.status));try{return JSON.parse(t)}catch(n){var r=n;throw r.name="ServerParseError",r.response=e,r.statusCode=e.status,r.bodyText=t,r}}function tZ(e,t){var n,r;"AbortError"!==e.name&&(e.result&&e.result.errors&&e.result.data&&(null===(n=t.next)||void 0===n||n.call(t,e.result)),null===(r=t.error)||void 0===r||r.call(t,e))}function tX(e,t,n){tJ(t)(e).then(function(e){var t,r;null===(t=n.next)||void 0===t||t.call(n,e),null===(r=n.complete)||void 0===r||r.call(n)}).catch(function(e){return tZ(e,n)})}function tJ(e){return function(t){return t.text().then(function(e){return tq(t,e)}).then(function(n){return t.status>=300&&tD(t,n,"Response not successful: Received status code ".concat(t.status)),Array.isArray(n)||tW.call(n,"data")||tW.call(n,"errors")||tD(t,n,"Server response was missing for query '".concat(Array.isArray(e)?e.map(function(e){return e.operationName}):e.operationName,"'.")),n})}}var tQ=function(e){if(!e&&"undefined"==typeof fetch)throw __DEV__?new Q.ej("\n\"fetch\" has not been found globally and no fetcher has been configured. To fix this, install a fetch package (like https://www.npmjs.com/package/cross-fetch), instantiate the fetcher, and pass it into your HttpLink constructor. For example:\n\nimport fetch from 'cross-fetch';\nimport { ApolloClient, HttpLink } from '@apollo/client';\nconst client = new ApolloClient({\n link: new HttpLink({ uri: '/graphql', fetch })\n});\n "):new Q.ej(23)},t1=__webpack_require__(87392);function t0(e){return tl(e,{leave:t3})}var t2=80,t3={Name:function(e){return e.value},Variable:function(e){return"$"+e.name},Document:function(e){return t6(e.definitions,"\n\n")+"\n"},OperationDefinition:function(e){var t=e.operation,n=e.name,r=t8("(",t6(e.variableDefinitions,", "),")"),i=t6(e.directives," "),a=e.selectionSet;return n||i||r||"query"!==t?t6([t,t6([n,r]),i,a]," "):a},VariableDefinition:function(e){var t=e.variable,n=e.type,r=e.defaultValue,i=e.directives;return t+": "+n+t8(" = ",r)+t8(" ",t6(i," "))},SelectionSet:function(e){return t5(e.selections)},Field:function(e){var t=e.alias,n=e.name,r=e.arguments,i=e.directives,a=e.selectionSet,o=t8("",t,": ")+n,s=o+t8("(",t6(r,", "),")");return s.length>t2&&(s=o+t8("(\n",t9(t6(r,"\n")),"\n)")),t6([s,t6(i," "),a]," ")},Argument:function(e){var t;return e.name+": "+e.value},FragmentSpread:function(e){var t;return"..."+e.name+t8(" ",t6(e.directives," "))},InlineFragment:function(e){var t=e.typeCondition,n=e.directives,r=e.selectionSet;return t6(["...",t8("on ",t),t6(n," "),r]," ")},FragmentDefinition:function(e){var t=e.name,n=e.typeCondition,r=e.variableDefinitions,i=e.directives,a=e.selectionSet;return"fragment ".concat(t).concat(t8("(",t6(r,", "),")")," ")+"on ".concat(n," ").concat(t8("",t6(i," ")," "))+a},IntValue:function(e){return e.value},FloatValue:function(e){return e.value},StringValue:function(e,t){var n=e.value;return e.block?(0,t1.LZ)(n,"description"===t?"":" "):JSON.stringify(n)},BooleanValue:function(e){return e.value?"true":"false"},NullValue:function(){return"null"},EnumValue:function(e){return e.value},ListValue:function(e){return"["+t6(e.values,", ")+"]"},ObjectValue:function(e){return"{"+t6(e.fields,", ")+"}"},ObjectField:function(e){var t;return e.name+": "+e.value},Directive:function(e){var t;return"@"+e.name+t8("(",t6(e.arguments,", "),")")},NamedType:function(e){return e.name},ListType:function(e){return"["+e.type+"]"},NonNullType:function(e){return e.type+"!"},SchemaDefinition:t4(function(e){var t=e.directives,n=e.operationTypes;return t6(["schema",t6(t," "),t5(n)]," ")}),OperationTypeDefinition:function(e){var t;return e.operation+": "+e.type},ScalarTypeDefinition:t4(function(e){var t;return t6(["scalar",e.name,t6(e.directives," ")]," ")}),ObjectTypeDefinition:t4(function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t6(["type",t,t8("implements ",t6(n," & ")),t6(r," "),t5(i)]," ")}),FieldDefinition:t4(function(e){var t=e.name,n=e.arguments,r=e.type,i=e.directives;return t+(ne(n)?t8("(\n",t9(t6(n,"\n")),"\n)"):t8("(",t6(n,", "),")"))+": "+r+t8(" ",t6(i," "))}),InputValueDefinition:t4(function(e){var t=e.name,n=e.type,r=e.defaultValue,i=e.directives;return t6([t+": "+n,t8("= ",r),t6(i," ")]," ")}),InterfaceTypeDefinition:t4(function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t6(["interface",t,t8("implements ",t6(n," & ")),t6(r," "),t5(i)]," ")}),UnionTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.types;return t6(["union",t,t6(n," "),r&&0!==r.length?"= "+t6(r," | "):""]," ")}),EnumTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.values;return t6(["enum",t,t6(n," "),t5(r)]," ")}),EnumValueDefinition:t4(function(e){var t;return t6([e.name,t6(e.directives," ")]," ")}),InputObjectTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.fields;return t6(["input",t,t6(n," "),t5(r)]," ")}),DirectiveDefinition:t4(function(e){var t=e.name,n=e.arguments,r=e.repeatable,i=e.locations;return"directive @"+t+(ne(n)?t8("(\n",t9(t6(n,"\n")),"\n)"):t8("(",t6(n,", "),")"))+(r?" repeatable":"")+" on "+t6(i," | ")}),SchemaExtension:function(e){var t=e.directives,n=e.operationTypes;return t6(["extend schema",t6(t," "),t5(n)]," ")},ScalarTypeExtension:function(e){var t;return t6(["extend scalar",e.name,t6(e.directives," ")]," ")},ObjectTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t6(["extend type",t,t8("implements ",t6(n," & ")),t6(r," "),t5(i)]," ")},InterfaceTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t6(["extend interface",t,t8("implements ",t6(n," & ")),t6(r," "),t5(i)]," ")},UnionTypeExtension:function(e){var t=e.name,n=e.directives,r=e.types;return t6(["extend union",t,t6(n," "),r&&0!==r.length?"= "+t6(r," | "):""]," ")},EnumTypeExtension:function(e){var t=e.name,n=e.directives,r=e.values;return t6(["extend enum",t,t6(n," "),t5(r)]," ")},InputObjectTypeExtension:function(e){var t=e.name,n=e.directives,r=e.fields;return t6(["extend input",t,t6(n," "),t5(r)]," ")}};function t4(e){return function(t){return t6([t.description,e(t)],"\n")}}function t6(e){var t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return null!==(t=null==e?void 0:e.filter(function(e){return e}).join(n))&&void 0!==t?t:""}function t5(e){return t8("{\n",t9(t6(e,"\n")),"\n}")}function t8(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return null!=t&&""!==t?e+t+n:""}function t9(e){return t8(" ",e.replace(/\n/g,"\n "))}function t7(e){return -1!==e.indexOf("\n")}function ne(e){return null!=e&&e.some(t7)}var nt,nn,nr,ni={http:{includeQuery:!0,includeExtensions:!1,preserveHeaderCase:!1},headers:{accept:"*/*","content-type":"application/json"},options:{method:"POST"}},na=function(e,t){return t(e)};function no(e,t){for(var n=[],r=2;rObject.create(null),{forEach:nv,slice:ny}=Array.prototype,{hasOwnProperty:nw}=Object.prototype;class n_{constructor(e=!0,t=ng){this.weakness=e,this.makeData=t}lookup(...e){return this.lookupArray(e)}lookupArray(e){let t=this;return nv.call(e,e=>t=t.getChildTrie(e)),nw.call(t,"data")?t.data:t.data=this.makeData(ny.call(e))}peek(...e){return this.peekArray(e)}peekArray(e){let t=this;for(let n=0,r=e.length;t&&n=0;--o)t.definitions[o].kind===nL.h.OPERATION_DEFINITION&&++a;var s=nN(e),u=e.some(function(e){return e.remove}),c=function(e){return u&&e&&e.some(s)},l=new Map,f=!1,d={enter:function(e){if(c(e.directives))return f=!0,null}},h=tl(t,{Field:d,InlineFragment:d,VariableDefinition:{enter:function(){return!1}},Variable:{enter:function(e,t,n,r,a){var o=i(a);o&&o.variables.add(e.name.value)}},FragmentSpread:{enter:function(e,t,n,r,a){if(c(e.directives))return f=!0,null;var o=i(a);o&&o.fragmentSpreads.add(e.name.value)}},FragmentDefinition:{enter:function(e,t,n,r){l.set(JSON.stringify(r),e)},leave:function(e,t,n,i){return e===l.get(JSON.stringify(i))?e:a>0&&e.selectionSet.selections.every(function(e){return e.kind===nL.h.FIELD&&"__typename"===e.name.value})?(r(e.name.value).removed=!0,f=!0,null):void 0}},Directive:{leave:function(e){if(s(e))return f=!0,null}}});if(!f)return t;var p=function(e){return e.transitiveVars||(e.transitiveVars=new Set(e.variables),e.removed||e.fragmentSpreads.forEach(function(t){p(r(t)).transitiveVars.forEach(function(t){e.transitiveVars.add(t)})})),e},b=new Set;h.definitions.forEach(function(e){e.kind===nL.h.OPERATION_DEFINITION?p(n(e.name&&e.name.value)).fragmentSpreads.forEach(function(e){b.add(e)}):e.kind!==nL.h.FRAGMENT_DEFINITION||0!==a||r(e.name.value).removed||b.add(e.name.value)}),b.forEach(function(e){p(r(e)).fragmentSpreads.forEach(function(e){b.add(e)})});var m=function(e){return!!(!b.has(e)||r(e).removed)},g={enter:function(e){if(m(e.name.value))return null}};return nD(tl(h,{FragmentSpread:g,FragmentDefinition:g,OperationDefinition:{leave:function(e){if(e.variableDefinitions){var t=p(n(e.name&&e.name.value)).transitiveVars;if(t.size0},t.prototype.tearDownQuery=function(){this.isTornDown||(this.concast&&this.observer&&(this.concast.removeObserver(this.observer),delete this.concast,delete this.observer),this.stopPolling(),this.subscriptions.forEach(function(e){return e.unsubscribe()}),this.subscriptions.clear(),this.queryManager.stopQuery(this.queryId),this.observers.clear(),this.isTornDown=!0)},t}(eT);function n4(e){var t=e.options,n=t.fetchPolicy,r=t.nextFetchPolicy;return"cache-and-network"===n||"network-only"===n?e.reobserve({fetchPolicy:"cache-first",nextFetchPolicy:function(){return(this.nextFetchPolicy=r,"function"==typeof r)?r.apply(this,arguments):n}}):e.reobserve()}function n6(e){__DEV__&&Q.kG.error("Unhandled error",e.message,e.stack)}function n5(e){__DEV__&&e&&__DEV__&&Q.kG.debug("Missing cache result fields: ".concat(JSON.stringify(e)),e)}function n8(e){return"network-only"===e||"no-cache"===e||"standby"===e}nK(n3);function n9(e){return e.kind===nL.h.FIELD||e.kind===nL.h.FRAGMENT_SPREAD||e.kind===nL.h.INLINE_FRAGMENT}function n7(e){return e.kind===Kind.SCALAR_TYPE_DEFINITION||e.kind===Kind.OBJECT_TYPE_DEFINITION||e.kind===Kind.INTERFACE_TYPE_DEFINITION||e.kind===Kind.UNION_TYPE_DEFINITION||e.kind===Kind.ENUM_TYPE_DEFINITION||e.kind===Kind.INPUT_OBJECT_TYPE_DEFINITION}function re(e){return e.kind===Kind.SCALAR_TYPE_EXTENSION||e.kind===Kind.OBJECT_TYPE_EXTENSION||e.kind===Kind.INTERFACE_TYPE_EXTENSION||e.kind===Kind.UNION_TYPE_EXTENSION||e.kind===Kind.ENUM_TYPE_EXTENSION||e.kind===Kind.INPUT_OBJECT_TYPE_EXTENSION}var rt=function(){return Object.create(null)},rn=Array.prototype,rr=rn.forEach,ri=rn.slice,ra=function(){function e(e,t){void 0===e&&(e=!0),void 0===t&&(t=rt),this.weakness=e,this.makeData=t}return e.prototype.lookup=function(){for(var e=[],t=0;tclass{constructor(){this.id=["slot",rc++,Date.now(),Math.random().toString(36).slice(2),].join(":")}hasValue(){for(let e=rs;e;e=e.parent)if(this.id in e.slots){let t=e.slots[this.id];if(t===ru)break;return e!==rs&&(rs.slots[this.id]=t),!0}return rs&&(rs.slots[this.id]=ru),!1}getValue(){if(this.hasValue())return rs.slots[this.id]}withValue(e,t,n,r){let i={__proto__:null,[this.id]:e},a=rs;rs={parent:a,slots:i};try{return t.apply(r,n)}finally{rs=a}}static bind(e){let t=rs;return function(){let n=rs;try{return rs=t,e.apply(this,arguments)}finally{rs=n}}}static noContext(e,t,n){if(!rs)return e.apply(n,t);{let r=rs;try{return rs=null,e.apply(n,t)}finally{rs=r}}}};function rf(e){try{return e()}catch(t){}}let rd="@wry/context:Slot",rh=rf(()=>globalThis)||rf(()=>global)||Object.create(null),rp=rh,rb=rp[rd]||Array[rd]||function(e){try{Object.defineProperty(rp,rd,{value:e,enumerable:!1,writable:!1,configurable:!0})}finally{return e}}(rl()),{bind:rm,noContext:rg}=rb;function rv(){}var ry=function(){function e(e,t){void 0===e&&(e=1/0),void 0===t&&(t=rv),this.max=e,this.dispose=t,this.map=new Map,this.newest=null,this.oldest=null}return e.prototype.has=function(e){return this.map.has(e)},e.prototype.get=function(e){var t=this.getNode(e);return t&&t.value},e.prototype.getNode=function(e){var t=this.map.get(e);if(t&&t!==this.newest){var n=t.older,r=t.newer;r&&(r.older=n),n&&(n.newer=r),t.older=this.newest,t.older.newer=t,t.newer=null,this.newest=t,t===this.oldest&&(this.oldest=r)}return t},e.prototype.set=function(e,t){var n=this.getNode(e);return n?n.value=t:(n={key:e,value:t,newer:null,older:this.newest},this.newest&&(this.newest.newer=n),this.newest=n,this.oldest=this.oldest||n,this.map.set(e,n),n.value)},e.prototype.clean=function(){for(;this.oldest&&this.map.size>this.max;)this.delete(this.oldest.key)},e.prototype.delete=function(e){var t=this.map.get(e);return!!t&&(t===this.newest&&(this.newest=t.older),t===this.oldest&&(this.oldest=t.newer),t.newer&&(t.newer.older=t.older),t.older&&(t.older.newer=t.newer),this.map.delete(e),this.dispose(t.value,e),!0)},e}(),rw=new rb,r_=Object.prototype.hasOwnProperty,rE=void 0===(n=Array.from)?function(e){var t=[];return e.forEach(function(e){return t.push(e)}),t}:n;function rS(e){var t=e.unsubscribe;"function"==typeof t&&(e.unsubscribe=void 0,t())}var rk=[],rx=100;function rT(e,t){if(!e)throw Error(t||"assertion failure")}function rM(e,t){var n=e.length;return n>0&&n===t.length&&e[n-1]===t[n-1]}function rO(e){switch(e.length){case 0:throw Error("unknown value");case 1:return e[0];case 2:throw e[1]}}function rA(e){return e.slice(0)}var rL=function(){function e(t){this.fn=t,this.parents=new Set,this.childValues=new Map,this.dirtyChildren=null,this.dirty=!0,this.recomputing=!1,this.value=[],this.deps=null,++e.count}return e.prototype.peek=function(){if(1===this.value.length&&!rN(this))return rC(this),this.value[0]},e.prototype.recompute=function(e){return rT(!this.recomputing,"already recomputing"),rC(this),rN(this)?rI(this,e):rO(this.value)},e.prototype.setDirty=function(){this.dirty||(this.dirty=!0,this.value.length=0,rR(this),rS(this))},e.prototype.dispose=function(){var e=this;this.setDirty(),rH(this),rF(this,function(t,n){t.setDirty(),r$(t,e)})},e.prototype.forget=function(){this.dispose()},e.prototype.dependOn=function(e){e.add(this),this.deps||(this.deps=rk.pop()||new Set),this.deps.add(e)},e.prototype.forgetDeps=function(){var e=this;this.deps&&(rE(this.deps).forEach(function(t){return t.delete(e)}),this.deps.clear(),rk.push(this.deps),this.deps=null)},e.count=0,e}();function rC(e){var t=rw.getValue();if(t)return e.parents.add(t),t.childValues.has(e)||t.childValues.set(e,[]),rN(e)?rY(t,e):rB(t,e),t}function rI(e,t){return rH(e),rw.withValue(e,rD,[e,t]),rz(e,t)&&rP(e),rO(e.value)}function rD(e,t){e.recomputing=!0,e.value.length=0;try{e.value[0]=e.fn.apply(null,t)}catch(n){e.value[1]=n}e.recomputing=!1}function rN(e){return e.dirty||!!(e.dirtyChildren&&e.dirtyChildren.size)}function rP(e){e.dirty=!1,!rN(e)&&rj(e)}function rR(e){rF(e,rY)}function rj(e){rF(e,rB)}function rF(e,t){var n=e.parents.size;if(n)for(var r=rE(e.parents),i=0;i0&&e.childValues.forEach(function(t,n){r$(e,n)}),e.forgetDeps(),rT(null===e.dirtyChildren)}function r$(e,t){t.parents.delete(e),e.childValues.delete(t),rU(e,t)}function rz(e,t){if("function"==typeof e.subscribe)try{rS(e),e.unsubscribe=e.subscribe.apply(null,t)}catch(n){return e.setDirty(),!1}return!0}var rG={setDirty:!0,dispose:!0,forget:!0};function rW(e){var t=new Map,n=e&&e.subscribe;function r(e){var r=rw.getValue();if(r){var i=t.get(e);i||t.set(e,i=new Set),r.dependOn(i),"function"==typeof n&&(rS(i),i.unsubscribe=n(e))}}return r.dirty=function(e,n){var r=t.get(e);if(r){var i=n&&r_.call(rG,n)?n:"setDirty";rE(r).forEach(function(e){return e[i]()}),t.delete(e),rS(r)}},r}function rK(){var e=new ra("function"==typeof WeakMap);return function(){return e.lookupArray(arguments)}}var rV=rK(),rq=new Set;function rZ(e,t){void 0===t&&(t=Object.create(null));var n=new ry(t.max||65536,function(e){return e.dispose()}),r=t.keyArgs,i=t.makeCacheKey||rK(),a=function(){var a=i.apply(null,r?r.apply(null,arguments):arguments);if(void 0===a)return e.apply(null,arguments);var o=n.get(a);o||(n.set(a,o=new rL(e)),o.subscribe=t.subscribe,o.forget=function(){return n.delete(a)});var s=o.recompute(Array.prototype.slice.call(arguments));return n.set(a,o),rq.add(n),rw.hasValue()||(rq.forEach(function(e){return e.clean()}),rq.clear()),s};function o(e){var t=n.get(e);t&&t.setDirty()}function s(e){var t=n.get(e);if(t)return t.peek()}function u(e){return n.delete(e)}return Object.defineProperty(a,"size",{get:function(){return n.map.size},configurable:!1,enumerable:!1}),a.dirtyKey=o,a.dirty=function(){o(i.apply(null,arguments))},a.peekKey=s,a.peek=function(){return s(i.apply(null,arguments))},a.forgetKey=u,a.forget=function(){return u(i.apply(null,arguments))},a.makeCacheKey=i,a.getKey=r?function(){return i.apply(null,r.apply(null,arguments))}:i,Object.freeze(a)}var rX=new rb,rJ=new WeakMap;function rQ(e){var t=rJ.get(e);return t||rJ.set(e,t={vars:new Set,dep:rW()}),t}function r1(e){rQ(e).vars.forEach(function(t){return t.forgetCache(e)})}function r0(e){rQ(e).vars.forEach(function(t){return t.attachCache(e)})}function r2(e){var t=new Set,n=new Set,r=function(a){if(arguments.length>0){if(e!==a){e=a,t.forEach(function(e){rQ(e).dep.dirty(r),r3(e)});var o=Array.from(n);n.clear(),o.forEach(function(t){return t(e)})}}else{var s=rX.getValue();s&&(i(s),rQ(s).dep(r))}return e};r.onNextChange=function(e){return n.add(e),function(){n.delete(e)}};var i=r.attachCache=function(e){return t.add(e),rQ(e).vars.add(r),r};return r.forgetCache=function(e){return t.delete(e)},r}function r3(e){e.broadcastWatches&&e.broadcastWatches()}var r4=function(){function e(e){var t=e.cache,n=e.client,r=e.resolvers,i=e.fragmentMatcher;this.selectionsToResolveCache=new WeakMap,this.cache=t,n&&(this.client=n),r&&this.addResolvers(r),i&&this.setFragmentMatcher(i)}return e.prototype.addResolvers=function(e){var t=this;this.resolvers=this.resolvers||{},Array.isArray(e)?e.forEach(function(e){t.resolvers=tj(t.resolvers,e)}):this.resolvers=tj(this.resolvers,e)},e.prototype.setResolvers=function(e){this.resolvers={},this.addResolvers(e)},e.prototype.getResolvers=function(){return this.resolvers||{}},e.prototype.runResolvers=function(e){var t=e.document,n=e.remoteResult,r=e.context,i=e.variables,a=e.onlyRunForcedResolvers,o=void 0!==a&&a;return(0,en.mG)(this,void 0,void 0,function(){return(0,en.Jh)(this,function(e){return t?[2,this.resolveDocument(t,n.data,r,i,this.fragmentMatcher,o).then(function(e){return(0,en.pi)((0,en.pi)({},n),{data:e.result})})]:[2,n]})})},e.prototype.setFragmentMatcher=function(e){this.fragmentMatcher=e},e.prototype.getFragmentMatcher=function(){return this.fragmentMatcher},e.prototype.clientQuery=function(e){return tb(["client"],e)&&this.resolvers?e:null},e.prototype.serverQuery=function(e){return n$(e)},e.prototype.prepareContext=function(e){var t=this.cache;return(0,en.pi)((0,en.pi)({},e),{cache:t,getCacheKey:function(e){return t.identify(e)}})},e.prototype.addExportedVariables=function(e,t,n){return void 0===t&&(t={}),void 0===n&&(n={}),(0,en.mG)(this,void 0,void 0,function(){return(0,en.Jh)(this,function(r){return e?[2,this.resolveDocument(e,this.buildRootValueFromCache(e,t)||{},this.prepareContext(n),t).then(function(e){return(0,en.pi)((0,en.pi)({},t),e.exportedVariables)})]:[2,(0,en.pi)({},t)]})})},e.prototype.shouldForceResolvers=function(e){var t=!1;return tl(e,{Directive:{enter:function(e){if("client"===e.name.value&&e.arguments&&(t=e.arguments.some(function(e){return"always"===e.name.value&&"BooleanValue"===e.value.kind&&!0===e.value.value})))return tc}}}),t},e.prototype.buildRootValueFromCache=function(e,t){return this.cache.diff({query:nH(e),variables:t,returnPartialData:!0,optimistic:!1}).result},e.prototype.resolveDocument=function(e,t,n,r,i,a){return void 0===n&&(n={}),void 0===r&&(r={}),void 0===i&&(i=function(){return!0}),void 0===a&&(a=!1),(0,en.mG)(this,void 0,void 0,function(){var o,s,u,c,l,f,d,h,p,b,m;return(0,en.Jh)(this,function(g){return o=e8(e),s=e4(e),u=eL(s),c=this.collectSelectionsToResolve(o,u),f=(l=o.operation)?l.charAt(0).toUpperCase()+l.slice(1):"Query",d=this,h=d.cache,p=d.client,b={fragmentMap:u,context:(0,en.pi)((0,en.pi)({},n),{cache:h,client:p}),variables:r,fragmentMatcher:i,defaultOperationType:f,exportedVariables:{},selectionsToResolve:c,onlyRunForcedResolvers:a},m=!1,[2,this.resolveSelectionSet(o.selectionSet,m,t,b).then(function(e){return{result:e,exportedVariables:b.exportedVariables}})]})})},e.prototype.resolveSelectionSet=function(e,t,n,r){return(0,en.mG)(this,void 0,void 0,function(){var i,a,o,s,u,c=this;return(0,en.Jh)(this,function(l){return i=r.fragmentMap,a=r.context,o=r.variables,s=[n],u=function(e){return(0,en.mG)(c,void 0,void 0,function(){var u,c;return(0,en.Jh)(this,function(l){return(t||r.selectionsToResolve.has(e))&&td(e,o)?eQ(e)?[2,this.resolveField(e,t,n,r).then(function(t){var n;void 0!==t&&s.push(((n={})[eX(e)]=t,n))})]:(e1(e)?u=e:(u=i[e.name.value],__DEV__?(0,Q.kG)(u,"No fragment named ".concat(e.name.value)):(0,Q.kG)(u,11)),u&&u.typeCondition&&(c=u.typeCondition.name.value,r.fragmentMatcher(n,c,a)))?[2,this.resolveSelectionSet(u.selectionSet,t,n,r).then(function(e){s.push(e)})]:[2]:[2]})})},[2,Promise.all(e.selections.map(u)).then(function(){return tF(s)})]})})},e.prototype.resolveField=function(e,t,n,r){return(0,en.mG)(this,void 0,void 0,function(){var i,a,o,s,u,c,l,f,d,h=this;return(0,en.Jh)(this,function(p){return n?(i=r.variables,a=e.name.value,o=eX(e),s=a!==o,c=Promise.resolve(u=n[o]||n[a]),(!r.onlyRunForcedResolvers||this.shouldForceResolvers(e))&&(l=n.__typename||r.defaultOperationType,(f=this.resolvers&&this.resolvers[l])&&(d=f[s?a:o])&&(c=Promise.resolve(rX.withValue(this.cache,d,[n,eZ(e,i),r.context,{field:e,fragmentMap:r.fragmentMap},])))),[2,c.then(function(n){if(void 0===n&&(n=u),e.directives&&e.directives.forEach(function(e){"export"===e.name.value&&e.arguments&&e.arguments.forEach(function(e){"as"===e.name.value&&"StringValue"===e.value.kind&&(r.exportedVariables[e.value.value]=n)})}),!e.selectionSet||null==n)return n;var i,a,o=null!==(a=null===(i=e.directives)||void 0===i?void 0:i.some(function(e){return"client"===e.name.value}))&&void 0!==a&&a;return Array.isArray(n)?h.resolveSubSelectedArray(e,t||o,n,r):e.selectionSet?h.resolveSelectionSet(e.selectionSet,t||o,n,r):void 0})]):[2,null]})})},e.prototype.resolveSubSelectedArray=function(e,t,n,r){var i=this;return Promise.all(n.map(function(n){return null===n?null:Array.isArray(n)?i.resolveSubSelectedArray(e,t,n,r):e.selectionSet?i.resolveSelectionSet(e.selectionSet,t,n,r):void 0}))},e.prototype.collectSelectionsToResolve=function(e,t){var n=function(e){return!Array.isArray(e)},r=this.selectionsToResolveCache;function i(e){if(!r.has(e)){var a=new Set;r.set(e,a),tl(e,{Directive:function(e,t,r,i,o){"client"===e.name.value&&o.forEach(function(e){n(e)&&n9(e)&&a.add(e)})},FragmentSpread:function(e,r,o,s,u){var c=t[e.name.value];__DEV__?(0,Q.kG)(c,"No fragment named ".concat(e.name.value)):(0,Q.kG)(c,12);var l=i(c);l.size>0&&(u.forEach(function(e){n(e)&&n9(e)&&a.add(e)}),a.add(e),l.forEach(function(e){a.add(e)}))}})}return r.get(e)}return i(e)},e}(),r6=new(t_.mr?WeakMap:Map);function r5(e,t){var n=e[t];"function"==typeof n&&(e[t]=function(){return r6.set(e,(r6.get(e)+1)%1e15),n.apply(this,arguments)})}function r8(e){e.notifyTimeout&&(clearTimeout(e.notifyTimeout),e.notifyTimeout=void 0)}var r9=function(){function e(e,t){void 0===t&&(t=e.generateQueryId()),this.queryId=t,this.listeners=new Set,this.document=null,this.lastRequestId=1,this.subscriptions=new Set,this.stopped=!1,this.dirty=!1,this.observableQuery=null;var n=this.cache=e.cache;r6.has(n)||(r6.set(n,0),r5(n,"evict"),r5(n,"modify"),r5(n,"reset"))}return e.prototype.init=function(e){var t=e.networkStatus||nZ.I.loading;return this.variables&&this.networkStatus!==nZ.I.loading&&!(0,nm.D)(this.variables,e.variables)&&(t=nZ.I.setVariables),(0,nm.D)(e.variables,this.variables)||(this.lastDiff=void 0),Object.assign(this,{document:e.document,variables:e.variables,networkError:null,graphQLErrors:this.graphQLErrors||[],networkStatus:t}),e.observableQuery&&this.setObservableQuery(e.observableQuery),e.lastRequestId&&(this.lastRequestId=e.lastRequestId),this},e.prototype.reset=function(){r8(this),this.dirty=!1},e.prototype.getDiff=function(e){void 0===e&&(e=this.variables);var t=this.getDiffOptions(e);if(this.lastDiff&&(0,nm.D)(t,this.lastDiff.options))return this.lastDiff.diff;this.updateWatch(this.variables=e);var n=this.observableQuery;if(n&&"no-cache"===n.options.fetchPolicy)return{complete:!1};var r=this.cache.diff(t);return this.updateLastDiff(r,t),r},e.prototype.updateLastDiff=function(e,t){this.lastDiff=e?{diff:e,options:t||this.getDiffOptions()}:void 0},e.prototype.getDiffOptions=function(e){var t;return void 0===e&&(e=this.variables),{query:this.document,variables:e,returnPartialData:!0,optimistic:!0,canonizeResults:null===(t=this.observableQuery)||void 0===t?void 0:t.options.canonizeResults}},e.prototype.setDiff=function(e){var t=this,n=this.lastDiff&&this.lastDiff.diff;this.updateLastDiff(e),this.dirty||(0,nm.D)(n&&n.result,e&&e.result)||(this.dirty=!0,this.notifyTimeout||(this.notifyTimeout=setTimeout(function(){return t.notify()},0)))},e.prototype.setObservableQuery=function(e){var t=this;e!==this.observableQuery&&(this.oqListener&&this.listeners.delete(this.oqListener),this.observableQuery=e,e?(e.queryInfo=this,this.listeners.add(this.oqListener=function(){t.getDiff().fromOptimisticTransaction?e.observe():n4(e)})):delete this.oqListener)},e.prototype.notify=function(){var e=this;r8(this),this.shouldNotify()&&this.listeners.forEach(function(t){return t(e)}),this.dirty=!1},e.prototype.shouldNotify=function(){if(!this.dirty||!this.listeners.size)return!1;if((0,nZ.O)(this.networkStatus)&&this.observableQuery){var e=this.observableQuery.options.fetchPolicy;if("cache-only"!==e&&"cache-and-network"!==e)return!1}return!0},e.prototype.stop=function(){if(!this.stopped){this.stopped=!0,this.reset(),this.cancel(),this.cancel=e.prototype.cancel,this.subscriptions.forEach(function(e){return e.unsubscribe()});var t=this.observableQuery;t&&t.stopPolling()}},e.prototype.cancel=function(){},e.prototype.updateWatch=function(e){var t=this;void 0===e&&(e=this.variables);var n=this.observableQuery;if(!n||"no-cache"!==n.options.fetchPolicy){var r=(0,en.pi)((0,en.pi)({},this.getDiffOptions(e)),{watcher:this,callback:function(e){return t.setDiff(e)}});this.lastWatch&&(0,nm.D)(r,this.lastWatch)||(this.cancel(),this.cancel=this.cache.watch(this.lastWatch=r))}},e.prototype.resetLastWrite=function(){this.lastWrite=void 0},e.prototype.shouldWrite=function(e,t){var n=this.lastWrite;return!(n&&n.dmCount===r6.get(this.cache)&&(0,nm.D)(t,n.variables)&&(0,nm.D)(e.data,n.result.data))},e.prototype.markResult=function(e,t,n,r){var i=this,a=new tB,o=(0,tP.O)(e.errors)?e.errors.slice(0):[];if(this.reset(),"incremental"in e&&(0,tP.O)(e.incremental)){var s=tG(this.getDiff().result,e);e.data=s}else if("hasNext"in e&&e.hasNext){var u=this.getDiff();e.data=a.merge(u.result,e.data)}this.graphQLErrors=o,"no-cache"===n.fetchPolicy?this.updateLastDiff({result:e.data,complete:!0},this.getDiffOptions(n.variables)):0!==r&&(r7(e,n.errorPolicy)?this.cache.performTransaction(function(a){if(i.shouldWrite(e,n.variables))a.writeQuery({query:t,data:e.data,variables:n.variables,overwrite:1===r}),i.lastWrite={result:e,variables:n.variables,dmCount:r6.get(i.cache)};else if(i.lastDiff&&i.lastDiff.diff.complete){e.data=i.lastDiff.diff.result;return}var o=i.getDiffOptions(n.variables),s=a.diff(o);i.stopped||i.updateWatch(n.variables),i.updateLastDiff(s,o),s.complete&&(e.data=s.result)}):this.lastWrite=void 0)},e.prototype.markReady=function(){return this.networkError=null,this.networkStatus=nZ.I.ready},e.prototype.markError=function(e){return this.networkStatus=nZ.I.error,this.lastWrite=void 0,this.reset(),e.graphQLErrors&&(this.graphQLErrors=e.graphQLErrors),e.networkError&&(this.networkError=e.networkError),e},e}();function r7(e,t){void 0===t&&(t="none");var n="ignore"===t||"all"===t,r=!nO(e);return!r&&n&&e.data&&(r=!0),r}var ie=Object.prototype.hasOwnProperty,it=function(){function e(e){var t=e.cache,n=e.link,r=e.defaultOptions,i=e.queryDeduplication,a=void 0!==i&&i,o=e.onBroadcast,s=e.ssrMode,u=void 0!==s&&s,c=e.clientAwareness,l=void 0===c?{}:c,f=e.localState,d=e.assumeImmutableResults;this.clientAwareness={},this.queries=new Map,this.fetchCancelFns=new Map,this.transformCache=new(t_.mr?WeakMap:Map),this.queryIdCounter=1,this.requestIdCounter=1,this.mutationIdCounter=1,this.inFlightLinkObservables=new Map,this.cache=t,this.link=n,this.defaultOptions=r||Object.create(null),this.queryDeduplication=a,this.clientAwareness=l,this.localState=f||new r4({cache:t}),this.ssrMode=u,this.assumeImmutableResults=!!d,(this.onBroadcast=o)&&(this.mutationStore=Object.create(null))}return e.prototype.stop=function(){var e=this;this.queries.forEach(function(t,n){e.stopQueryNoBroadcast(n)}),this.cancelPendingFetches(__DEV__?new Q.ej("QueryManager stopped while query was in flight"):new Q.ej(14))},e.prototype.cancelPendingFetches=function(e){this.fetchCancelFns.forEach(function(t){return t(e)}),this.fetchCancelFns.clear()},e.prototype.mutate=function(e){var t,n,r=e.mutation,i=e.variables,a=e.optimisticResponse,o=e.updateQueries,s=e.refetchQueries,u=void 0===s?[]:s,c=e.awaitRefetchQueries,l=void 0!==c&&c,f=e.update,d=e.onQueryUpdated,h=e.fetchPolicy,p=void 0===h?(null===(t=this.defaultOptions.mutate)||void 0===t?void 0:t.fetchPolicy)||"network-only":h,b=e.errorPolicy,m=void 0===b?(null===(n=this.defaultOptions.mutate)||void 0===n?void 0:n.errorPolicy)||"none":b,g=e.keepRootFields,v=e.context;return(0,en.mG)(this,void 0,void 0,function(){var e,t,n,s,c,h;return(0,en.Jh)(this,function(b){switch(b.label){case 0:if(__DEV__?(0,Q.kG)(r,"mutation option is required. You must specify your GraphQL document in the mutation option."):(0,Q.kG)(r,15),__DEV__?(0,Q.kG)("network-only"===p||"no-cache"===p,"Mutations support only 'network-only' or 'no-cache' fetchPolicy strings. The default `network-only` behavior automatically writes mutation results to the cache. Passing `no-cache` skips the cache write."):(0,Q.kG)("network-only"===p||"no-cache"===p,16),e=this.generateMutationId(),n=(t=this.transform(r)).document,s=t.hasClientExports,r=this.cache.transformForLink(n),i=this.getVariables(r,i),!s)return[3,2];return[4,this.localState.addExportedVariables(r,i,v)];case 1:i=b.sent(),b.label=2;case 2:return c=this.mutationStore&&(this.mutationStore[e]={mutation:r,variables:i,loading:!0,error:null}),a&&this.markMutationOptimistic(a,{mutationId:e,document:r,variables:i,fetchPolicy:p,errorPolicy:m,context:v,updateQueries:o,update:f,keepRootFields:g}),this.broadcastQueries(),h=this,[2,new Promise(function(t,n){return nM(h.getObservableFromLink(r,(0,en.pi)((0,en.pi)({},v),{optimisticResponse:a}),i,!1),function(t){if(nO(t)&&"none"===m)throw new tN.cA({graphQLErrors:nA(t)});c&&(c.loading=!1,c.error=null);var n=(0,en.pi)({},t);return"function"==typeof u&&(u=u(n)),"ignore"===m&&nO(n)&&delete n.errors,h.markMutationResult({mutationId:e,result:n,document:r,variables:i,fetchPolicy:p,errorPolicy:m,context:v,update:f,updateQueries:o,awaitRefetchQueries:l,refetchQueries:u,removeOptimistic:a?e:void 0,onQueryUpdated:d,keepRootFields:g})}).subscribe({next:function(e){h.broadcastQueries(),"hasNext"in e&&!1!==e.hasNext||t(e)},error:function(t){c&&(c.loading=!1,c.error=t),a&&h.cache.removeOptimistic(e),h.broadcastQueries(),n(t instanceof tN.cA?t:new tN.cA({networkError:t}))}})})]}})})},e.prototype.markMutationResult=function(e,t){var n=this;void 0===t&&(t=this.cache);var r=e.result,i=[],a="no-cache"===e.fetchPolicy;if(!a&&r7(r,e.errorPolicy)){if(tU(r)||i.push({result:r.data,dataId:"ROOT_MUTATION",query:e.document,variables:e.variables}),tU(r)&&(0,tP.O)(r.incremental)){var o=t.diff({id:"ROOT_MUTATION",query:this.transform(e.document).asQuery,variables:e.variables,optimistic:!1,returnPartialData:!0}),s=void 0;o.result&&(s=tG(o.result,r)),void 0!==s&&(r.data=s,i.push({result:s,dataId:"ROOT_MUTATION",query:e.document,variables:e.variables}))}var u=e.updateQueries;u&&this.queries.forEach(function(e,a){var o=e.observableQuery,s=o&&o.queryName;if(s&&ie.call(u,s)){var c,l=u[s],f=n.queries.get(a),d=f.document,h=f.variables,p=t.diff({query:d,variables:h,returnPartialData:!0,optimistic:!1}),b=p.result;if(p.complete&&b){var m=l(b,{mutationResult:r,queryName:d&&e3(d)||void 0,queryVariables:h});m&&i.push({result:m,dataId:"ROOT_QUERY",query:d,variables:h})}}})}if(i.length>0||e.refetchQueries||e.update||e.onQueryUpdated||e.removeOptimistic){var c=[];if(this.refetchQueries({updateCache:function(t){a||i.forEach(function(e){return t.write(e)});var o=e.update,s=!t$(r)||tU(r)&&!r.hasNext;if(o){if(!a){var u=t.diff({id:"ROOT_MUTATION",query:n.transform(e.document).asQuery,variables:e.variables,optimistic:!1,returnPartialData:!0});u.complete&&("incremental"in(r=(0,en.pi)((0,en.pi)({},r),{data:u.result}))&&delete r.incremental,"hasNext"in r&&delete r.hasNext)}s&&o(t,r,{context:e.context,variables:e.variables})}a||e.keepRootFields||!s||t.modify({id:"ROOT_MUTATION",fields:function(e,t){var n=t.fieldName,r=t.DELETE;return"__typename"===n?e:r}})},include:e.refetchQueries,optimistic:!1,removeOptimistic:e.removeOptimistic,onQueryUpdated:e.onQueryUpdated||null}).forEach(function(e){return c.push(e)}),e.awaitRefetchQueries||e.onQueryUpdated)return Promise.all(c).then(function(){return r})}return Promise.resolve(r)},e.prototype.markMutationOptimistic=function(e,t){var n=this,r="function"==typeof e?e(t.variables):e;return this.cache.recordOptimisticTransaction(function(e){try{n.markMutationResult((0,en.pi)((0,en.pi)({},t),{result:{data:r}}),e)}catch(i){__DEV__&&Q.kG.error(i)}},t.mutationId)},e.prototype.fetchQuery=function(e,t,n){return this.fetchQueryObservable(e,t,n).promise},e.prototype.getQueryStore=function(){var e=Object.create(null);return this.queries.forEach(function(t,n){e[n]={variables:t.variables,networkStatus:t.networkStatus,networkError:t.networkError,graphQLErrors:t.graphQLErrors}}),e},e.prototype.resetErrors=function(e){var t=this.queries.get(e);t&&(t.networkError=void 0,t.graphQLErrors=[])},e.prototype.transform=function(e){var t=this.transformCache;if(!t.has(e)){var n=this.cache.transformDocument(e),r=nY(n),i=this.localState.clientQuery(n),a=r&&this.localState.serverQuery(r),o={document:n,hasClientExports:tm(n),hasForcedResolvers:this.localState.shouldForceResolvers(n),clientQuery:i,serverQuery:a,defaultVars:e9(e2(n)),asQuery:(0,en.pi)((0,en.pi)({},n),{definitions:n.definitions.map(function(e){return"OperationDefinition"===e.kind&&"query"!==e.operation?(0,en.pi)((0,en.pi)({},e),{operation:"query"}):e})})},s=function(e){e&&!t.has(e)&&t.set(e,o)};s(e),s(n),s(i),s(a)}return t.get(e)},e.prototype.getVariables=function(e,t){return(0,en.pi)((0,en.pi)({},this.transform(e).defaultVars),t)},e.prototype.watchQuery=function(e){void 0===(e=(0,en.pi)((0,en.pi)({},e),{variables:this.getVariables(e.query,e.variables)})).notifyOnNetworkStatusChange&&(e.notifyOnNetworkStatusChange=!1);var t=new r9(this),n=new n3({queryManager:this,queryInfo:t,options:e});return this.queries.set(n.queryId,t),t.init({document:n.query,observableQuery:n,variables:n.variables}),n},e.prototype.query=function(e,t){var n=this;return void 0===t&&(t=this.generateQueryId()),__DEV__?(0,Q.kG)(e.query,"query option is required. You must specify your GraphQL document in the query option."):(0,Q.kG)(e.query,17),__DEV__?(0,Q.kG)("Document"===e.query.kind,'You must wrap the query string in a "gql" tag.'):(0,Q.kG)("Document"===e.query.kind,18),__DEV__?(0,Q.kG)(!e.returnPartialData,"returnPartialData option only supported on watchQuery."):(0,Q.kG)(!e.returnPartialData,19),__DEV__?(0,Q.kG)(!e.pollInterval,"pollInterval option only supported on watchQuery."):(0,Q.kG)(!e.pollInterval,20),this.fetchQuery(t,e).finally(function(){return n.stopQuery(t)})},e.prototype.generateQueryId=function(){return String(this.queryIdCounter++)},e.prototype.generateRequestId=function(){return this.requestIdCounter++},e.prototype.generateMutationId=function(){return String(this.mutationIdCounter++)},e.prototype.stopQueryInStore=function(e){this.stopQueryInStoreNoBroadcast(e),this.broadcastQueries()},e.prototype.stopQueryInStoreNoBroadcast=function(e){var t=this.queries.get(e);t&&t.stop()},e.prototype.clearStore=function(e){return void 0===e&&(e={discardWatches:!0}),this.cancelPendingFetches(__DEV__?new Q.ej("Store reset while query was in flight (not completed in link chain)"):new Q.ej(21)),this.queries.forEach(function(e){e.observableQuery?e.networkStatus=nZ.I.loading:e.stop()}),this.mutationStore&&(this.mutationStore=Object.create(null)),this.cache.reset(e)},e.prototype.getObservableQueries=function(e){var t=this;void 0===e&&(e="active");var n=new Map,r=new Map,i=new Set;return Array.isArray(e)&&e.forEach(function(e){"string"==typeof e?r.set(e,!1):eN(e)?r.set(t.transform(e).document,!1):(0,eO.s)(e)&&e.query&&i.add(e)}),this.queries.forEach(function(t,i){var a=t.observableQuery,o=t.document;if(a){if("all"===e){n.set(i,a);return}var s=a.queryName;if("standby"===a.options.fetchPolicy||"active"===e&&!a.hasObservers())return;("active"===e||s&&r.has(s)||o&&r.has(o))&&(n.set(i,a),s&&r.set(s,!0),o&&r.set(o,!0))}}),i.size&&i.forEach(function(e){var r=nG("legacyOneTimeQuery"),i=t.getQuery(r).init({document:e.query,variables:e.variables}),a=new n3({queryManager:t,queryInfo:i,options:(0,en.pi)((0,en.pi)({},e),{fetchPolicy:"network-only"})});(0,Q.kG)(a.queryId===r),i.setObservableQuery(a),n.set(r,a)}),__DEV__&&r.size&&r.forEach(function(e,t){!e&&__DEV__&&Q.kG.warn("Unknown query ".concat("string"==typeof t?"named ":"").concat(JSON.stringify(t,null,2)," requested in refetchQueries options.include array"))}),n},e.prototype.reFetchObservableQueries=function(e){var t=this;void 0===e&&(e=!1);var n=[];return this.getObservableQueries(e?"all":"active").forEach(function(r,i){var a=r.options.fetchPolicy;r.resetLastResults(),(e||"standby"!==a&&"cache-only"!==a)&&n.push(r.refetch()),t.getQuery(i).setDiff(null)}),this.broadcastQueries(),Promise.all(n)},e.prototype.setObservableQuery=function(e){this.getQuery(e.queryId).setObservableQuery(e)},e.prototype.startGraphQLSubscription=function(e){var t=this,n=e.query,r=e.fetchPolicy,i=e.errorPolicy,a=e.variables,o=e.context,s=void 0===o?{}:o;n=this.transform(n).document,a=this.getVariables(n,a);var u=function(e){return t.getObservableFromLink(n,s,e).map(function(a){"no-cache"!==r&&(r7(a,i)&&t.cache.write({query:n,result:a.data,dataId:"ROOT_SUBSCRIPTION",variables:e}),t.broadcastQueries());var o=nO(a),s=(0,tN.ls)(a);if(o||s){var u={};throw o&&(u.graphQLErrors=a.errors),s&&(u.protocolErrors=a.extensions[tN.YG]),new tN.cA(u)}return a})};if(this.transform(n).hasClientExports){var c=this.localState.addExportedVariables(n,a,s).then(u);return new eT(function(e){var t=null;return c.then(function(n){return t=n.subscribe(e)},e.error),function(){return t&&t.unsubscribe()}})}return u(a)},e.prototype.stopQuery=function(e){this.stopQueryNoBroadcast(e),this.broadcastQueries()},e.prototype.stopQueryNoBroadcast=function(e){this.stopQueryInStoreNoBroadcast(e),this.removeQuery(e)},e.prototype.removeQuery=function(e){this.fetchCancelFns.delete(e),this.queries.has(e)&&(this.getQuery(e).stop(),this.queries.delete(e))},e.prototype.broadcastQueries=function(){this.onBroadcast&&this.onBroadcast(),this.queries.forEach(function(e){return e.notify()})},e.prototype.getLocalState=function(){return this.localState},e.prototype.getObservableFromLink=function(e,t,n,r){var i,a,o=this;void 0===r&&(r=null!==(i=null==t?void 0:t.queryDeduplication)&&void 0!==i?i:this.queryDeduplication);var s=this.transform(e).serverQuery;if(s){var u=this,c=u.inFlightLinkObservables,l=u.link,f={query:s,variables:n,operationName:e3(s)||void 0,context:this.prepareContext((0,en.pi)((0,en.pi)({},t),{forceFetch:!r}))};if(t=f.context,r){var d=c.get(s)||new Map;c.set(s,d);var h=nx(n);if(!(a=d.get(h))){var p=new nq([np(l,f)]);d.set(h,a=p),p.beforeNext(function(){d.delete(h)&&d.size<1&&c.delete(s)})}}else a=new nq([np(l,f)])}else a=new nq([eT.of({data:{}})]),t=this.prepareContext(t);var b=this.transform(e).clientQuery;return b&&(a=nM(a,function(e){return o.localState.runResolvers({document:b,remoteResult:e,context:t,variables:n})})),a},e.prototype.getResultsFromLink=function(e,t,n){var r=e.lastRequestId=this.generateRequestId(),i=this.cache.transformForLink(this.transform(e.document).document);return nM(this.getObservableFromLink(i,n.context,n.variables),function(a){var o=nA(a),s=o.length>0;if(r>=e.lastRequestId){if(s&&"none"===n.errorPolicy)throw e.markError(new tN.cA({graphQLErrors:o}));e.markResult(a,i,n,t),e.markReady()}var u={data:a.data,loading:!1,networkStatus:nZ.I.ready};return s&&"ignore"!==n.errorPolicy&&(u.errors=o,u.networkStatus=nZ.I.error),u},function(t){var n=(0,tN.MS)(t)?t:new tN.cA({networkError:t});throw r>=e.lastRequestId&&e.markError(n),n})},e.prototype.fetchQueryObservable=function(e,t,n){return this.fetchConcastWithInfo(e,t,n).concast},e.prototype.fetchConcastWithInfo=function(e,t,n){var r,i,a=this;void 0===n&&(n=nZ.I.loading);var o=this.transform(t.query).document,s=this.getVariables(o,t.variables),u=this.getQuery(e),c=this.defaultOptions.watchQuery,l=t.fetchPolicy,f=void 0===l?c&&c.fetchPolicy||"cache-first":l,d=t.errorPolicy,h=void 0===d?c&&c.errorPolicy||"none":d,p=t.returnPartialData,b=void 0!==p&&p,m=t.notifyOnNetworkStatusChange,g=void 0!==m&&m,v=t.context,y=void 0===v?{}:v,w=Object.assign({},t,{query:o,variables:s,fetchPolicy:f,errorPolicy:h,returnPartialData:b,notifyOnNetworkStatusChange:g,context:y}),_=function(e){w.variables=e;var r=a.fetchQueryByPolicy(u,w,n);return"standby"!==w.fetchPolicy&&r.sources.length>0&&u.observableQuery&&u.observableQuery.applyNextFetchPolicy("after-fetch",t),r},E=function(){return a.fetchCancelFns.delete(e)};if(this.fetchCancelFns.set(e,function(e){E(),setTimeout(function(){return r.cancel(e)})}),this.transform(w.query).hasClientExports)r=new nq(this.localState.addExportedVariables(w.query,w.variables,w.context).then(_).then(function(e){return e.sources})),i=!0;else{var S=_(w.variables);i=S.fromLink,r=new nq(S.sources)}return r.promise.then(E,E),{concast:r,fromLink:i}},e.prototype.refetchQueries=function(e){var t=this,n=e.updateCache,r=e.include,i=e.optimistic,a=void 0!==i&&i,o=e.removeOptimistic,s=void 0===o?a?nG("refetchQueries"):void 0:o,u=e.onQueryUpdated,c=new Map;r&&this.getObservableQueries(r).forEach(function(e,n){c.set(n,{oq:e,lastDiff:t.getQuery(n).getDiff()})});var l=new Map;return n&&this.cache.batch({update:n,optimistic:a&&s||!1,removeOptimistic:s,onWatchUpdated:function(e,t,n){var r=e.watcher instanceof r9&&e.watcher.observableQuery;if(r){if(u){c.delete(r.queryId);var i=u(r,t,n);return!0===i&&(i=r.refetch()),!1!==i&&l.set(r,i),i}null!==u&&c.set(r.queryId,{oq:r,lastDiff:n,diff:t})}}}),c.size&&c.forEach(function(e,n){var r,i=e.oq,a=e.lastDiff,o=e.diff;if(u){if(!o){var s=i.queryInfo;s.reset(),o=s.getDiff()}r=u(i,o,a)}u&&!0!==r||(r=i.refetch()),!1!==r&&l.set(i,r),n.indexOf("legacyOneTimeQuery")>=0&&t.stopQueryNoBroadcast(n)}),s&&this.cache.removeOptimistic(s),l},e.prototype.fetchQueryByPolicy=function(e,t,n){var r=this,i=t.query,a=t.variables,o=t.fetchPolicy,s=t.refetchWritePolicy,u=t.errorPolicy,c=t.returnPartialData,l=t.context,f=t.notifyOnNetworkStatusChange,d=e.networkStatus;e.init({document:this.transform(i).document,variables:a,networkStatus:n});var h=function(){return e.getDiff(a)},p=function(t,n){void 0===n&&(n=e.networkStatus||nZ.I.loading);var o=t.result;!__DEV__||c||(0,nm.D)(o,{})||n5(t.missing);var s=function(e){return eT.of((0,en.pi)({data:e,loading:(0,nZ.O)(n),networkStatus:n},t.complete?null:{partial:!0}))};return o&&r.transform(i).hasForcedResolvers?r.localState.runResolvers({document:i,remoteResult:{data:o},context:l,variables:a,onlyRunForcedResolvers:!0}).then(function(e){return s(e.data||void 0)}):"none"===u&&n===nZ.I.refetch&&Array.isArray(t.missing)?s(void 0):s(o)},b="no-cache"===o?0:n===nZ.I.refetch&&"merge"!==s?1:2,m=function(){return r.getResultsFromLink(e,b,{variables:a,context:l,fetchPolicy:o,errorPolicy:u})},g=f&&"number"==typeof d&&d!==n&&(0,nZ.O)(n);switch(o){default:case"cache-first":var v=h();if(v.complete)return{fromLink:!1,sources:[p(v,e.markReady())]};if(c||g)return{fromLink:!0,sources:[p(v),m()]};return{fromLink:!0,sources:[m()]};case"cache-and-network":var v=h();if(v.complete||c||g)return{fromLink:!0,sources:[p(v),m()]};return{fromLink:!0,sources:[m()]};case"cache-only":return{fromLink:!1,sources:[p(h(),e.markReady())]};case"network-only":if(g)return{fromLink:!0,sources:[p(h()),m()]};return{fromLink:!0,sources:[m()]};case"no-cache":if(g)return{fromLink:!0,sources:[p(e.getDiff()),m(),]};return{fromLink:!0,sources:[m()]};case"standby":return{fromLink:!1,sources:[]}}},e.prototype.getQuery=function(e){return e&&!this.queries.has(e)&&this.queries.set(e,new r9(this,e)),this.queries.get(e)},e.prototype.prepareContext=function(e){void 0===e&&(e={});var t=this.localState.prepareContext(e);return(0,en.pi)((0,en.pi)({},t),{clientAwareness:this.clientAwareness})},e}(),ir=__webpack_require__(14012),ii=!1,ia=function(){function e(e){var t=this;this.resetStoreCallbacks=[],this.clearStoreCallbacks=[];var n=e.uri,r=e.credentials,i=e.headers,a=e.cache,o=e.ssrMode,s=void 0!==o&&o,u=e.ssrForceFetchDelay,c=void 0===u?0:u,l=e.connectToDevTools,f=void 0===l?"object"==typeof window&&!window.__APOLLO_CLIENT__&&__DEV__:l,d=e.queryDeduplication,h=void 0===d||d,p=e.defaultOptions,b=e.assumeImmutableResults,m=void 0!==b&&b,g=e.resolvers,v=e.typeDefs,y=e.fragmentMatcher,w=e.name,_=e.version,E=e.link;if(E||(E=n?new nh({uri:n,credentials:r,headers:i}):ta.empty()),!a)throw __DEV__?new Q.ej("To initialize Apollo Client, you must specify a 'cache' property in the options object. \nFor more information, please visit: https://go.apollo.dev/c/docs"):new Q.ej(9);if(this.link=E,this.cache=a,this.disableNetworkFetches=s||c>0,this.queryDeduplication=h,this.defaultOptions=p||Object.create(null),this.typeDefs=v,c&&setTimeout(function(){return t.disableNetworkFetches=!1},c),this.watchQuery=this.watchQuery.bind(this),this.query=this.query.bind(this),this.mutate=this.mutate.bind(this),this.resetStore=this.resetStore.bind(this),this.reFetchObservableQueries=this.reFetchObservableQueries.bind(this),f&&"object"==typeof window&&(window.__APOLLO_CLIENT__=this),!ii&&f&&__DEV__&&(ii=!0,"undefined"!=typeof window&&window.document&&window.top===window.self&&!window.__APOLLO_DEVTOOLS_GLOBAL_HOOK__)){var S=window.navigator,k=S&&S.userAgent,x=void 0;"string"==typeof k&&(k.indexOf("Chrome/")>-1?x="https://chrome.google.com/webstore/detail/apollo-client-developer-t/jdkknkkbebbapilgoeccciglkfbmbnfm":k.indexOf("Firefox/")>-1&&(x="https://addons.mozilla.org/en-US/firefox/addon/apollo-developer-tools/")),x&&__DEV__&&Q.kG.log("Download the Apollo DevTools for a better development experience: "+x)}this.version=nb,this.localState=new r4({cache:a,client:this,resolvers:g,fragmentMatcher:y}),this.queryManager=new it({cache:this.cache,link:this.link,defaultOptions:this.defaultOptions,queryDeduplication:h,ssrMode:s,clientAwareness:{name:w,version:_},localState:this.localState,assumeImmutableResults:m,onBroadcast:f?function(){t.devToolsHookCb&&t.devToolsHookCb({action:{},state:{queries:t.queryManager.getQueryStore(),mutations:t.queryManager.mutationStore||{}},dataWithOptimisticResults:t.cache.extract(!0)})}:void 0})}return e.prototype.stop=function(){this.queryManager.stop()},e.prototype.watchQuery=function(e){return this.defaultOptions.watchQuery&&(e=(0,ir.J)(this.defaultOptions.watchQuery,e)),this.disableNetworkFetches&&("network-only"===e.fetchPolicy||"cache-and-network"===e.fetchPolicy)&&(e=(0,en.pi)((0,en.pi)({},e),{fetchPolicy:"cache-first"})),this.queryManager.watchQuery(e)},e.prototype.query=function(e){return this.defaultOptions.query&&(e=(0,ir.J)(this.defaultOptions.query,e)),__DEV__?(0,Q.kG)("cache-and-network"!==e.fetchPolicy,"The cache-and-network fetchPolicy does not work with client.query, because client.query can only return a single result. Please use client.watchQuery to receive multiple results from the cache and the network, or consider using a different fetchPolicy, such as cache-first or network-only."):(0,Q.kG)("cache-and-network"!==e.fetchPolicy,10),this.disableNetworkFetches&&"network-only"===e.fetchPolicy&&(e=(0,en.pi)((0,en.pi)({},e),{fetchPolicy:"cache-first"})),this.queryManager.query(e)},e.prototype.mutate=function(e){return this.defaultOptions.mutate&&(e=(0,ir.J)(this.defaultOptions.mutate,e)),this.queryManager.mutate(e)},e.prototype.subscribe=function(e){return this.queryManager.startGraphQLSubscription(e)},e.prototype.readQuery=function(e,t){return void 0===t&&(t=!1),this.cache.readQuery(e,t)},e.prototype.readFragment=function(e,t){return void 0===t&&(t=!1),this.cache.readFragment(e,t)},e.prototype.writeQuery=function(e){var t=this.cache.writeQuery(e);return!1!==e.broadcast&&this.queryManager.broadcastQueries(),t},e.prototype.writeFragment=function(e){var t=this.cache.writeFragment(e);return!1!==e.broadcast&&this.queryManager.broadcastQueries(),t},e.prototype.__actionHookForDevTools=function(e){this.devToolsHookCb=e},e.prototype.__requestRaw=function(e){return np(this.link,e)},e.prototype.resetStore=function(){var e=this;return Promise.resolve().then(function(){return e.queryManager.clearStore({discardWatches:!1})}).then(function(){return Promise.all(e.resetStoreCallbacks.map(function(e){return e()}))}).then(function(){return e.reFetchObservableQueries()})},e.prototype.clearStore=function(){var e=this;return Promise.resolve().then(function(){return e.queryManager.clearStore({discardWatches:!0})}).then(function(){return Promise.all(e.clearStoreCallbacks.map(function(e){return e()}))})},e.prototype.onResetStore=function(e){var t=this;return this.resetStoreCallbacks.push(e),function(){t.resetStoreCallbacks=t.resetStoreCallbacks.filter(function(t){return t!==e})}},e.prototype.onClearStore=function(e){var t=this;return this.clearStoreCallbacks.push(e),function(){t.clearStoreCallbacks=t.clearStoreCallbacks.filter(function(t){return t!==e})}},e.prototype.reFetchObservableQueries=function(e){return this.queryManager.reFetchObservableQueries(e)},e.prototype.refetchQueries=function(e){var t=this.queryManager.refetchQueries(e),n=[],r=[];t.forEach(function(e,t){n.push(t),r.push(e)});var i=Promise.all(r);return i.queries=n,i.results=r,i.catch(function(e){__DEV__&&Q.kG.debug("In client.refetchQueries, Promise.all promise rejected with error ".concat(e))}),i},e.prototype.getObservableQueries=function(e){return void 0===e&&(e="active"),this.queryManager.getObservableQueries(e)},e.prototype.extract=function(e){return this.cache.extract(e)},e.prototype.restore=function(e){return this.cache.restore(e)},e.prototype.addResolvers=function(e){this.localState.addResolvers(e)},e.prototype.setResolvers=function(e){this.localState.setResolvers(e)},e.prototype.getResolvers=function(){return this.localState.getResolvers()},e.prototype.setLocalStateFragmentMatcher=function(e){this.localState.setFragmentMatcher(e)},e.prototype.setLink=function(e){this.link=this.queryManager.link=e},e}(),io=function(){function e(){this.getFragmentDoc=rZ(eA)}return e.prototype.batch=function(e){var t,n=this,r="string"==typeof e.optimistic?e.optimistic:!1===e.optimistic?null:void 0;return this.performTransaction(function(){return t=e.update(n)},r),t},e.prototype.recordOptimisticTransaction=function(e,t){this.performTransaction(e,t)},e.prototype.transformDocument=function(e){return e},e.prototype.transformForLink=function(e){return e},e.prototype.identify=function(e){},e.prototype.gc=function(){return[]},e.prototype.modify=function(e){return!1},e.prototype.readQuery=function(e,t){return void 0===t&&(t=!!e.optimistic),this.read((0,en.pi)((0,en.pi)({},e),{rootId:e.id||"ROOT_QUERY",optimistic:t}))},e.prototype.readFragment=function(e,t){return void 0===t&&(t=!!e.optimistic),this.read((0,en.pi)((0,en.pi)({},e),{query:this.getFragmentDoc(e.fragment,e.fragmentName),rootId:e.id,optimistic:t}))},e.prototype.writeQuery=function(e){var t=e.id,n=e.data,r=(0,en._T)(e,["id","data"]);return this.write(Object.assign(r,{dataId:t||"ROOT_QUERY",result:n}))},e.prototype.writeFragment=function(e){var t=e.id,n=e.data,r=e.fragment,i=e.fragmentName,a=(0,en._T)(e,["id","data","fragment","fragmentName"]);return this.write(Object.assign(a,{query:this.getFragmentDoc(r,i),dataId:t,result:n}))},e.prototype.updateQuery=function(e,t){return this.batch({update:function(n){var r=n.readQuery(e),i=t(r);return null==i?r:(n.writeQuery((0,en.pi)((0,en.pi)({},e),{data:i})),i)}})},e.prototype.updateFragment=function(e,t){return this.batch({update:function(n){var r=n.readFragment(e),i=t(r);return null==i?r:(n.writeFragment((0,en.pi)((0,en.pi)({},e),{data:i})),i)}})},e}(),is=function(e){function t(n,r,i,a){var o,s=e.call(this,n)||this;if(s.message=n,s.path=r,s.query=i,s.variables=a,Array.isArray(s.path)){s.missing=s.message;for(var u=s.path.length-1;u>=0;--u)s.missing=((o={})[s.path[u]]=s.missing,o)}else s.missing=s.path;return s.__proto__=t.prototype,s}return(0,en.ZT)(t,e),t}(Error),iu=__webpack_require__(10542),ic=Object.prototype.hasOwnProperty;function il(e){return null==e}function id(e,t){var n=e.__typename,r=e.id,i=e._id;if("string"==typeof n&&(t&&(t.keyObject=il(r)?il(i)?void 0:{_id:i}:{id:r}),il(r)&&!il(i)&&(r=i),!il(r)))return"".concat(n,":").concat("number"==typeof r||"string"==typeof r?r:JSON.stringify(r))}var ih={dataIdFromObject:id,addTypename:!0,resultCaching:!0,canonizeResults:!1};function ip(e){return(0,n1.o)(ih,e)}function ib(e){var t=e.canonizeResults;return void 0===t?ih.canonizeResults:t}function im(e,t){return eD(t)?e.get(t.__ref,"__typename"):t&&t.__typename}var ig=/^[_a-z][_0-9a-z]*/i;function iv(e){var t=e.match(ig);return t?t[0]:e}function iy(e,t,n){return!!(0,eO.s)(t)&&((0,tP.k)(t)?t.every(function(t){return iy(e,t,n)}):e.selections.every(function(e){if(eQ(e)&&td(e,n)){var r=eX(e);return ic.call(t,r)&&(!e.selectionSet||iy(e.selectionSet,t[r],n))}return!0}))}function iw(e){return(0,eO.s)(e)&&!eD(e)&&!(0,tP.k)(e)}function i_(){return new tB}function iE(e,t){var n=eL(e4(e));return{fragmentMap:n,lookupFragment:function(e){var r=n[e];return!r&&t&&(r=t.lookup(e)),r||null}}}var iS=Object.create(null),ik=function(){return iS},ix=Object.create(null),iT=function(){function e(e,t){var n=this;this.policies=e,this.group=t,this.data=Object.create(null),this.rootIds=Object.create(null),this.refs=Object.create(null),this.getFieldValue=function(e,t){return(0,iu.J)(eD(e)?n.get(e.__ref,t):e&&e[t])},this.canRead=function(e){return eD(e)?n.has(e.__ref):"object"==typeof e},this.toReference=function(e,t){if("string"==typeof e)return eI(e);if(eD(e))return e;var r=n.policies.identify(e)[0];if(r){var i=eI(r);return t&&n.merge(r,e),i}}}return e.prototype.toObject=function(){return(0,en.pi)({},this.data)},e.prototype.has=function(e){return void 0!==this.lookup(e,!0)},e.prototype.get=function(e,t){if(this.group.depend(e,t),ic.call(this.data,e)){var n=this.data[e];if(n&&ic.call(n,t))return n[t]}return"__typename"===t&&ic.call(this.policies.rootTypenamesById,e)?this.policies.rootTypenamesById[e]:this instanceof iL?this.parent.get(e,t):void 0},e.prototype.lookup=function(e,t){return(t&&this.group.depend(e,"__exists"),ic.call(this.data,e))?this.data[e]:this instanceof iL?this.parent.lookup(e,t):this.policies.rootTypenamesById[e]?Object.create(null):void 0},e.prototype.merge=function(e,t){var n,r=this;eD(e)&&(e=e.__ref),eD(t)&&(t=t.__ref);var i="string"==typeof e?this.lookup(n=e):e,a="string"==typeof t?this.lookup(n=t):t;if(a){__DEV__?(0,Q.kG)("string"==typeof n,"store.merge expects a string ID"):(0,Q.kG)("string"==typeof n,1);var o=new tB(iI).merge(i,a);if(this.data[n]=o,o!==i&&(delete this.refs[n],this.group.caching)){var s=Object.create(null);i||(s.__exists=1),Object.keys(a).forEach(function(e){if(!i||i[e]!==o[e]){s[e]=1;var t=iv(e);t===e||r.policies.hasKeyArgs(o.__typename,t)||(s[t]=1),void 0!==o[e]||r instanceof iL||delete o[e]}}),s.__typename&&!(i&&i.__typename)&&this.policies.rootTypenamesById[n]===o.__typename&&delete s.__typename,Object.keys(s).forEach(function(e){return r.group.dirty(n,e)})}}},e.prototype.modify=function(e,t){var n=this,r=this.lookup(e);if(r){var i=Object.create(null),a=!1,o=!0,s={DELETE:iS,INVALIDATE:ix,isReference:eD,toReference:this.toReference,canRead:this.canRead,readField:function(t,r){return n.policies.readField("string"==typeof t?{fieldName:t,from:r||eI(e)}:t,{store:n})}};if(Object.keys(r).forEach(function(u){var c=iv(u),l=r[u];if(void 0!==l){var f="function"==typeof t?t:t[u]||t[c];if(f){var d=f===ik?iS:f((0,iu.J)(l),(0,en.pi)((0,en.pi)({},s),{fieldName:c,storeFieldName:u,storage:n.getStorage(e,u)}));d===ix?n.group.dirty(e,u):(d===iS&&(d=void 0),d!==l&&(i[u]=d,a=!0,l=d))}void 0!==l&&(o=!1)}}),a)return this.merge(e,i),o&&(this instanceof iL?this.data[e]=void 0:delete this.data[e],this.group.dirty(e,"__exists")),!0}return!1},e.prototype.delete=function(e,t,n){var r,i=this.lookup(e);if(i){var a=this.getFieldValue(i,"__typename"),o=t&&n?this.policies.getStoreFieldName({typename:a,fieldName:t,args:n}):t;return this.modify(e,o?((r={})[o]=ik,r):ik)}return!1},e.prototype.evict=function(e,t){var n=!1;return e.id&&(ic.call(this.data,e.id)&&(n=this.delete(e.id,e.fieldName,e.args)),this instanceof iL&&this!==t&&(n=this.parent.evict(e,t)||n),(e.fieldName||n)&&this.group.dirty(e.id,e.fieldName||"__exists")),n},e.prototype.clear=function(){this.replace(null)},e.prototype.extract=function(){var e=this,t=this.toObject(),n=[];return this.getRootIdSet().forEach(function(t){ic.call(e.policies.rootTypenamesById,t)||n.push(t)}),n.length&&(t.__META={extraRootIds:n.sort()}),t},e.prototype.replace=function(e){var t=this;if(Object.keys(this.data).forEach(function(n){e&&ic.call(e,n)||t.delete(n)}),e){var n=e.__META,r=(0,en._T)(e,["__META"]);Object.keys(r).forEach(function(e){t.merge(e,r[e])}),n&&n.extraRootIds.forEach(this.retain,this)}},e.prototype.retain=function(e){return this.rootIds[e]=(this.rootIds[e]||0)+1},e.prototype.release=function(e){if(this.rootIds[e]>0){var t=--this.rootIds[e];return t||delete this.rootIds[e],t}return 0},e.prototype.getRootIdSet=function(e){return void 0===e&&(e=new Set),Object.keys(this.rootIds).forEach(e.add,e),this instanceof iL?this.parent.getRootIdSet(e):Object.keys(this.policies.rootTypenamesById).forEach(e.add,e),e},e.prototype.gc=function(){var e=this,t=this.getRootIdSet(),n=this.toObject();t.forEach(function(r){ic.call(n,r)&&(Object.keys(e.findChildRefIds(r)).forEach(t.add,t),delete n[r])});var r=Object.keys(n);if(r.length){for(var i=this;i instanceof iL;)i=i.parent;r.forEach(function(e){return i.delete(e)})}return r},e.prototype.findChildRefIds=function(e){if(!ic.call(this.refs,e)){var t=this.refs[e]=Object.create(null),n=this.data[e];if(!n)return t;var r=new Set([n]);r.forEach(function(e){eD(e)&&(t[e.__ref]=!0),(0,eO.s)(e)&&Object.keys(e).forEach(function(t){var n=e[t];(0,eO.s)(n)&&r.add(n)})})}return this.refs[e]},e.prototype.makeCacheKey=function(){return this.group.keyMaker.lookupArray(arguments)},e}(),iM=function(){function e(e,t){void 0===t&&(t=null),this.caching=e,this.parent=t,this.d=null,this.resetCaching()}return e.prototype.resetCaching=function(){this.d=this.caching?rW():null,this.keyMaker=new n_(t_.mr)},e.prototype.depend=function(e,t){if(this.d){this.d(iO(e,t));var n=iv(t);n!==t&&this.d(iO(e,n)),this.parent&&this.parent.depend(e,t)}},e.prototype.dirty=function(e,t){this.d&&this.d.dirty(iO(e,t),"__exists"===t?"forget":"setDirty")},e}();function iO(e,t){return t+"#"+e}function iA(e,t){iD(e)&&e.group.depend(t,"__exists")}!function(e){var t=function(e){function t(t){var n=t.policies,r=t.resultCaching,i=void 0===r||r,a=t.seed,o=e.call(this,n,new iM(i))||this;return o.stump=new iC(o),o.storageTrie=new n_(t_.mr),a&&o.replace(a),o}return(0,en.ZT)(t,e),t.prototype.addLayer=function(e,t){return this.stump.addLayer(e,t)},t.prototype.removeLayer=function(){return this},t.prototype.getStorage=function(){return this.storageTrie.lookupArray(arguments)},t}(e);e.Root=t}(iT||(iT={}));var iL=function(e){function t(t,n,r,i){var a=e.call(this,n.policies,i)||this;return a.id=t,a.parent=n,a.replay=r,a.group=i,r(a),a}return(0,en.ZT)(t,e),t.prototype.addLayer=function(e,n){return new t(e,this,n,this.group)},t.prototype.removeLayer=function(e){var t=this,n=this.parent.removeLayer(e);return e===this.id?(this.group.caching&&Object.keys(this.data).forEach(function(e){var r=t.data[e],i=n.lookup(e);i?r?r!==i&&Object.keys(r).forEach(function(n){(0,nm.D)(r[n],i[n])||t.group.dirty(e,n)}):(t.group.dirty(e,"__exists"),Object.keys(i).forEach(function(n){t.group.dirty(e,n)})):t.delete(e)}),n):n===this.parent?this:n.addLayer(this.id,this.replay)},t.prototype.toObject=function(){return(0,en.pi)((0,en.pi)({},this.parent.toObject()),this.data)},t.prototype.findChildRefIds=function(t){var n=this.parent.findChildRefIds(t);return ic.call(this.data,t)?(0,en.pi)((0,en.pi)({},n),e.prototype.findChildRefIds.call(this,t)):n},t.prototype.getStorage=function(){for(var e=this.parent;e.parent;)e=e.parent;return e.getStorage.apply(e,arguments)},t}(iT),iC=function(e){function t(t){return e.call(this,"EntityStore.Stump",t,function(){},new iM(t.group.caching,t.group))||this}return(0,en.ZT)(t,e),t.prototype.removeLayer=function(){return this},t.prototype.merge=function(){return this.parent.merge.apply(this.parent,arguments)},t}(iL);function iI(e,t,n){var r=e[n],i=t[n];return(0,nm.D)(r,i)?r:i}function iD(e){return!!(e instanceof iT&&e.group.caching)}function iN(e){return[e.selectionSet,e.objectOrReference,e.context,e.context.canonizeResults,]}var iP=function(){function e(e){var t=this;this.knownResults=new(t_.mr?WeakMap:Map),this.config=(0,n1.o)(e,{addTypename:!1!==e.addTypename,canonizeResults:ib(e)}),this.canon=e.canon||new nk,this.executeSelectionSet=rZ(function(e){var n,r=e.context.canonizeResults,i=iN(e);i[3]=!r;var a=(n=t.executeSelectionSet).peek.apply(n,i);return a?r?(0,en.pi)((0,en.pi)({},a),{result:t.canon.admit(a.result)}):a:(iA(e.context.store,e.enclosingRef.__ref),t.execSelectionSetImpl(e))},{max:this.config.resultCacheMaxSize,keyArgs:iN,makeCacheKey:function(e,t,n,r){if(iD(n.store))return n.store.makeCacheKey(e,eD(t)?t.__ref:t,n.varString,r)}}),this.executeSubSelectedArray=rZ(function(e){return iA(e.context.store,e.enclosingRef.__ref),t.execSubSelectedArrayImpl(e)},{max:this.config.resultCacheMaxSize,makeCacheKey:function(e){var t=e.field,n=e.array,r=e.context;if(iD(r.store))return r.store.makeCacheKey(t,n,r.varString)}})}return e.prototype.resetCanon=function(){this.canon=new nk},e.prototype.diffQueryAgainstStore=function(e){var t,n=e.store,r=e.query,i=e.rootId,a=void 0===i?"ROOT_QUERY":i,o=e.variables,s=e.returnPartialData,u=void 0===s||s,c=e.canonizeResults,l=void 0===c?this.config.canonizeResults:c,f=this.config.cache.policies;o=(0,en.pi)((0,en.pi)({},e9(e6(r))),o);var d=eI(a),h=this.executeSelectionSet({selectionSet:e8(r).selectionSet,objectOrReference:d,enclosingRef:d,context:(0,en.pi)({store:n,query:r,policies:f,variables:o,varString:nx(o),canonizeResults:l},iE(r,this.config.fragments))});if(h.missing&&(t=[new is(iR(h.missing),h.missing,r,o)],!u))throw t[0];return{result:h.result,complete:!t,missing:t}},e.prototype.isFresh=function(e,t,n,r){if(iD(r.store)&&this.knownResults.get(e)===n){var i=this.executeSelectionSet.peek(n,t,r,this.canon.isKnown(e));if(i&&e===i.result)return!0}return!1},e.prototype.execSelectionSetImpl=function(e){var t,n=this,r=e.selectionSet,i=e.objectOrReference,a=e.enclosingRef,o=e.context;if(eD(i)&&!o.policies.rootTypenamesById[i.__ref]&&!o.store.has(i.__ref))return{result:this.canon.empty,missing:"Dangling reference to missing ".concat(i.__ref," object")};var s=o.variables,u=o.policies,c=o.store.getFieldValue(i,"__typename"),l=[],f=new tB;function d(e,n){var r;return e.missing&&(t=f.merge(t,((r={})[n]=e.missing,r))),e.result}this.config.addTypename&&"string"==typeof c&&!u.rootIdsByTypename[c]&&l.push({__typename:c});var h=new Set(r.selections);h.forEach(function(e){var r,p;if(td(e,s)){if(eQ(e)){var b=u.readField({fieldName:e.name.value,field:e,variables:o.variables,from:i},o),m=eX(e);void 0===b?nj.added(e)||(t=f.merge(t,((r={})[m]="Can't find field '".concat(e.name.value,"' on ").concat(eD(i)?i.__ref+" object":"object "+JSON.stringify(i,null,2)),r))):(0,tP.k)(b)?b=d(n.executeSubSelectedArray({field:e,array:b,enclosingRef:a,context:o}),m):e.selectionSet?null!=b&&(b=d(n.executeSelectionSet({selectionSet:e.selectionSet,objectOrReference:b,enclosingRef:eD(b)?b:a,context:o}),m)):o.canonizeResults&&(b=n.canon.pass(b)),void 0!==b&&l.push(((p={})[m]=b,p))}else{var g=eC(e,o.lookupFragment);if(!g&&e.kind===nL.h.FRAGMENT_SPREAD)throw __DEV__?new Q.ej("No fragment named ".concat(e.name.value)):new Q.ej(5);g&&u.fragmentMatches(g,c)&&g.selectionSet.selections.forEach(h.add,h)}}});var p={result:tF(l),missing:t},b=o.canonizeResults?this.canon.admit(p):(0,iu.J)(p);return b.result&&this.knownResults.set(b.result,r),b},e.prototype.execSubSelectedArrayImpl=function(e){var t,n=this,r=e.field,i=e.array,a=e.enclosingRef,o=e.context,s=new tB;function u(e,n){var r;return e.missing&&(t=s.merge(t,((r={})[n]=e.missing,r))),e.result}return r.selectionSet&&(i=i.filter(o.store.canRead)),i=i.map(function(e,t){return null===e?null:(0,tP.k)(e)?u(n.executeSubSelectedArray({field:r,array:e,enclosingRef:a,context:o}),t):r.selectionSet?u(n.executeSelectionSet({selectionSet:r.selectionSet,objectOrReference:e,enclosingRef:eD(e)?e:a,context:o}),t):(__DEV__&&ij(o.store,r,e),e)}),{result:o.canonizeResults?this.canon.admit(i):i,missing:t}},e}();function iR(e){try{JSON.stringify(e,function(e,t){if("string"==typeof t)throw t;return t})}catch(t){return t}}function ij(e,t,n){if(!t.selectionSet){var r=new Set([n]);r.forEach(function(n){(0,eO.s)(n)&&(__DEV__?(0,Q.kG)(!eD(n),"Missing selection set for object of type ".concat(im(e,n)," returned for query field ").concat(t.name.value)):(0,Q.kG)(!eD(n),6),Object.values(n).forEach(r.add,r))})}}function iF(e){var t=nG("stringifyForDisplay");return JSON.stringify(e,function(e,n){return void 0===n?t:n}).split(JSON.stringify(t)).join("")}var iY=Object.create(null);function iB(e){var t=JSON.stringify(e);return iY[t]||(iY[t]=Object.create(null))}function iU(e){var t=iB(e);return t.keyFieldsFn||(t.keyFieldsFn=function(t,n){var r=function(e,t){return n.readField(t,e)},i=n.keyObject=i$(e,function(e){var i=iW(n.storeObject,e,r);return void 0===i&&t!==n.storeObject&&ic.call(t,e[0])&&(i=iW(t,e,iG)),__DEV__?(0,Q.kG)(void 0!==i,"Missing field '".concat(e.join("."),"' while extracting keyFields from ").concat(JSON.stringify(t))):(0,Q.kG)(void 0!==i,2),i});return"".concat(n.typename,":").concat(JSON.stringify(i))})}function iH(e){var t=iB(e);return t.keyArgsFn||(t.keyArgsFn=function(t,n){var r=n.field,i=n.variables,a=n.fieldName,o=JSON.stringify(i$(e,function(e){var n=e[0],a=n.charAt(0);if("@"===a){if(r&&(0,tP.O)(r.directives)){var o=n.slice(1),s=r.directives.find(function(e){return e.name.value===o}),u=s&&eZ(s,i);return u&&iW(u,e.slice(1))}return}if("$"===a){var c=n.slice(1);if(i&&ic.call(i,c)){var l=e.slice(0);return l[0]=c,iW(i,l)}return}if(t)return iW(t,e)}));return(t||"{}"!==o)&&(a+=":"+o),a})}function i$(e,t){var n=new tB;return iz(e).reduce(function(e,r){var i,a=t(r);if(void 0!==a){for(var o=r.length-1;o>=0;--o)a=((i={})[r[o]]=a,i);e=n.merge(e,a)}return e},Object.create(null))}function iz(e){var t=iB(e);if(!t.paths){var n=t.paths=[],r=[];e.forEach(function(t,i){(0,tP.k)(t)?(iz(t).forEach(function(e){return n.push(r.concat(e))}),r.length=0):(r.push(t),(0,tP.k)(e[i+1])||(n.push(r.slice(0)),r.length=0))})}return t.paths}function iG(e,t){return e[t]}function iW(e,t,n){return n=n||iG,iK(t.reduce(function e(t,r){return(0,tP.k)(t)?t.map(function(t){return e(t,r)}):t&&n(t,r)},e))}function iK(e){return(0,eO.s)(e)?(0,tP.k)(e)?e.map(iK):i$(Object.keys(e).sort(),function(t){return iW(e,t)}):e}function iV(e){return void 0!==e.args?e.args:e.field?eZ(e.field,e.variables):null}eK.setStringify(nx);var iq=function(){},iZ=function(e,t){return t.fieldName},iX=function(e,t,n){return(0,n.mergeObjects)(e,t)},iJ=function(e,t){return t},iQ=function(){function e(e){this.config=e,this.typePolicies=Object.create(null),this.toBeAdded=Object.create(null),this.supertypeMap=new Map,this.fuzzySubtypes=new Map,this.rootIdsByTypename=Object.create(null),this.rootTypenamesById=Object.create(null),this.usingPossibleTypes=!1,this.config=(0,en.pi)({dataIdFromObject:id},e),this.cache=this.config.cache,this.setRootTypename("Query"),this.setRootTypename("Mutation"),this.setRootTypename("Subscription"),e.possibleTypes&&this.addPossibleTypes(e.possibleTypes),e.typePolicies&&this.addTypePolicies(e.typePolicies)}return e.prototype.identify=function(e,t){var n,r,i=this,a=t&&(t.typename||(null===(n=t.storeObject)||void 0===n?void 0:n.__typename))||e.__typename;if(a===this.rootTypenamesById.ROOT_QUERY)return["ROOT_QUERY"];for(var o=t&&t.storeObject||e,s=(0,en.pi)((0,en.pi)({},t),{typename:a,storeObject:o,readField:t&&t.readField||function(){var e=i0(arguments,o);return i.readField(e,{store:i.cache.data,variables:e.variables})}}),u=a&&this.getTypePolicy(a),c=u&&u.keyFn||this.config.dataIdFromObject;c;){var l=c((0,en.pi)((0,en.pi)({},e),o),s);if((0,tP.k)(l))c=iU(l);else{r=l;break}}return r=r?String(r):void 0,s.keyObject?[r,s.keyObject]:[r]},e.prototype.addTypePolicies=function(e){var t=this;Object.keys(e).forEach(function(n){var r=e[n],i=r.queryType,a=r.mutationType,o=r.subscriptionType,s=(0,en._T)(r,["queryType","mutationType","subscriptionType"]);i&&t.setRootTypename("Query",n),a&&t.setRootTypename("Mutation",n),o&&t.setRootTypename("Subscription",n),ic.call(t.toBeAdded,n)?t.toBeAdded[n].push(s):t.toBeAdded[n]=[s]})},e.prototype.updateTypePolicy=function(e,t){var n=this,r=this.getTypePolicy(e),i=t.keyFields,a=t.fields;function o(e,t){e.merge="function"==typeof t?t:!0===t?iX:!1===t?iJ:e.merge}o(r,t.merge),r.keyFn=!1===i?iq:(0,tP.k)(i)?iU(i):"function"==typeof i?i:r.keyFn,a&&Object.keys(a).forEach(function(t){var r=n.getFieldPolicy(e,t,!0),i=a[t];if("function"==typeof i)r.read=i;else{var s=i.keyArgs,u=i.read,c=i.merge;r.keyFn=!1===s?iZ:(0,tP.k)(s)?iH(s):"function"==typeof s?s:r.keyFn,"function"==typeof u&&(r.read=u),o(r,c)}r.read&&r.merge&&(r.keyFn=r.keyFn||iZ)})},e.prototype.setRootTypename=function(e,t){void 0===t&&(t=e);var n="ROOT_"+e.toUpperCase(),r=this.rootTypenamesById[n];t!==r&&(__DEV__?(0,Q.kG)(!r||r===e,"Cannot change root ".concat(e," __typename more than once")):(0,Q.kG)(!r||r===e,3),r&&delete this.rootIdsByTypename[r],this.rootIdsByTypename[t]=n,this.rootTypenamesById[n]=t)},e.prototype.addPossibleTypes=function(e){var t=this;this.usingPossibleTypes=!0,Object.keys(e).forEach(function(n){t.getSupertypeSet(n,!0),e[n].forEach(function(e){t.getSupertypeSet(e,!0).add(n);var r=e.match(ig);r&&r[0]===e||t.fuzzySubtypes.set(e,RegExp(e))})})},e.prototype.getTypePolicy=function(e){var t=this;if(!ic.call(this.typePolicies,e)){var n=this.typePolicies[e]=Object.create(null);n.fields=Object.create(null);var r=this.supertypeMap.get(e);r&&r.size&&r.forEach(function(e){var r=t.getTypePolicy(e),i=r.fields;Object.assign(n,(0,en._T)(r,["fields"])),Object.assign(n.fields,i)})}var i=this.toBeAdded[e];return i&&i.length&&i.splice(0).forEach(function(n){t.updateTypePolicy(e,n)}),this.typePolicies[e]},e.prototype.getFieldPolicy=function(e,t,n){if(e){var r=this.getTypePolicy(e).fields;return r[t]||n&&(r[t]=Object.create(null))}},e.prototype.getSupertypeSet=function(e,t){var n=this.supertypeMap.get(e);return!n&&t&&this.supertypeMap.set(e,n=new Set),n},e.prototype.fragmentMatches=function(e,t,n,r){var i=this;if(!e.typeCondition)return!0;if(!t)return!1;var a=e.typeCondition.name.value;if(t===a)return!0;if(this.usingPossibleTypes&&this.supertypeMap.has(a))for(var o=this.getSupertypeSet(t,!0),s=[o],u=function(e){var t=i.getSupertypeSet(e,!1);t&&t.size&&0>s.indexOf(t)&&s.push(t)},c=!!(n&&this.fuzzySubtypes.size),l=!1,f=0;f1?a:t}:(r=(0,en.pi)({},i),ic.call(r,"from")||(r.from=t)),__DEV__&&void 0===r.from&&__DEV__&&Q.kG.warn("Undefined 'from' passed to readField with arguments ".concat(iF(Array.from(e)))),void 0===r.variables&&(r.variables=n),r}function i2(e){return function(t,n){if((0,tP.k)(t)||(0,tP.k)(n))throw __DEV__?new Q.ej("Cannot automatically merge arrays"):new Q.ej(4);if((0,eO.s)(t)&&(0,eO.s)(n)){var r=e.getFieldValue(t,"__typename"),i=e.getFieldValue(n,"__typename");if(r&&i&&r!==i)return n;if(eD(t)&&iw(n))return e.merge(t.__ref,n),t;if(iw(t)&&eD(n))return e.merge(t,n.__ref),n;if(iw(t)&&iw(n))return(0,en.pi)((0,en.pi)({},t),n)}return n}}function i3(e,t,n){var r="".concat(t).concat(n),i=e.flavors.get(r);return i||e.flavors.set(r,i=e.clientOnly===t&&e.deferred===n?e:(0,en.pi)((0,en.pi)({},e),{clientOnly:t,deferred:n})),i}var i4=function(){function e(e,t,n){this.cache=e,this.reader=t,this.fragments=n}return e.prototype.writeToStore=function(e,t){var n=this,r=t.query,i=t.result,a=t.dataId,o=t.variables,s=t.overwrite,u=e2(r),c=i_();o=(0,en.pi)((0,en.pi)({},e9(u)),o);var l=(0,en.pi)((0,en.pi)({store:e,written:Object.create(null),merge:function(e,t){return c.merge(e,t)},variables:o,varString:nx(o)},iE(r,this.fragments)),{overwrite:!!s,incomingById:new Map,clientOnly:!1,deferred:!1,flavors:new Map}),f=this.processSelectionSet({result:i||Object.create(null),dataId:a,selectionSet:u.selectionSet,mergeTree:{map:new Map},context:l});if(!eD(f))throw __DEV__?new Q.ej("Could not identify object ".concat(JSON.stringify(i))):new Q.ej(7);return l.incomingById.forEach(function(t,r){var i=t.storeObject,a=t.mergeTree,o=t.fieldNodeSet,s=eI(r);if(a&&a.map.size){var u=n.applyMerges(a,s,i,l);if(eD(u))return;i=u}if(__DEV__&&!l.overwrite){var c=Object.create(null);o.forEach(function(e){e.selectionSet&&(c[e.name.value]=!0)});var f=function(e){return!0===c[iv(e)]},d=function(e){var t=a&&a.map.get(e);return Boolean(t&&t.info&&t.info.merge)};Object.keys(i).forEach(function(e){f(e)&&!d(e)&&at(s,i,e,l.store)})}e.merge(r,i)}),e.retain(f.__ref),f},e.prototype.processSelectionSet=function(e){var t=this,n=e.dataId,r=e.result,i=e.selectionSet,a=e.context,o=e.mergeTree,s=this.cache.policies,u=Object.create(null),c=n&&s.rootTypenamesById[n]||eJ(r,i,a.fragmentMap)||n&&a.store.get(n,"__typename");"string"==typeof c&&(u.__typename=c);var l=function(){var e=i0(arguments,u,a.variables);if(eD(e.from)){var t=a.incomingById.get(e.from.__ref);if(t){var n=s.readField((0,en.pi)((0,en.pi)({},e),{from:t.storeObject}),a);if(void 0!==n)return n}}return s.readField(e,a)},f=new Set;this.flattenFields(i,r,a,c).forEach(function(e,n){var i,a=r[eX(n)];if(f.add(n),void 0!==a){var d=s.getStoreFieldName({typename:c,fieldName:n.name.value,field:n,variables:e.variables}),h=i5(o,d),p=t.processFieldValue(a,n,n.selectionSet?i3(e,!1,!1):e,h),b=void 0;n.selectionSet&&(eD(p)||iw(p))&&(b=l("__typename",p));var m=s.getMergeFunction(c,n.name.value,b);m?h.info={field:n,typename:c,merge:m}:i7(o,d),u=e.merge(u,((i={})[d]=p,i))}else __DEV__&&!e.clientOnly&&!e.deferred&&!nj.added(n)&&!s.getReadFunction(c,n.name.value)&&__DEV__&&Q.kG.error("Missing field '".concat(eX(n),"' while writing result ").concat(JSON.stringify(r,null,2)).substring(0,1e3))});try{var d=s.identify(r,{typename:c,selectionSet:i,fragmentMap:a.fragmentMap,storeObject:u,readField:l}),h=d[0],p=d[1];n=n||h,p&&(u=a.merge(u,p))}catch(b){if(!n)throw b}if("string"==typeof n){var m=eI(n),g=a.written[n]||(a.written[n]=[]);if(g.indexOf(i)>=0||(g.push(i),this.reader&&this.reader.isFresh(r,m,i,a)))return m;var v=a.incomingById.get(n);return v?(v.storeObject=a.merge(v.storeObject,u),v.mergeTree=i8(v.mergeTree,o),f.forEach(function(e){return v.fieldNodeSet.add(e)})):a.incomingById.set(n,{storeObject:u,mergeTree:i9(o)?void 0:o,fieldNodeSet:f}),m}return u},e.prototype.processFieldValue=function(e,t,n,r){var i=this;return t.selectionSet&&null!==e?(0,tP.k)(e)?e.map(function(e,a){var o=i.processFieldValue(e,t,n,i5(r,a));return i7(r,a),o}):this.processSelectionSet({result:e,selectionSet:t.selectionSet,context:n,mergeTree:r}):__DEV__?nJ(e):e},e.prototype.flattenFields=function(e,t,n,r){void 0===r&&(r=eJ(t,e,n.fragmentMap));var i=new Map,a=this.cache.policies,o=new n_(!1);return function e(s,u){var c=o.lookup(s,u.clientOnly,u.deferred);c.visited||(c.visited=!0,s.selections.forEach(function(o){if(td(o,n.variables)){var s=u.clientOnly,c=u.deferred;if(!(s&&c)&&(0,tP.O)(o.directives)&&o.directives.forEach(function(e){var t=e.name.value;if("client"===t&&(s=!0),"defer"===t){var r=eZ(e,n.variables);r&&!1===r.if||(c=!0)}}),eQ(o)){var l=i.get(o);l&&(s=s&&l.clientOnly,c=c&&l.deferred),i.set(o,i3(n,s,c))}else{var f=eC(o,n.lookupFragment);if(!f&&o.kind===nL.h.FRAGMENT_SPREAD)throw __DEV__?new Q.ej("No fragment named ".concat(o.name.value)):new Q.ej(8);f&&a.fragmentMatches(f,r,t,n.variables)&&e(f.selectionSet,i3(n,s,c))}}}))}(e,n),i},e.prototype.applyMerges=function(e,t,n,r,i){var a=this;if(e.map.size&&!eD(n)){var o,s,u=!(0,tP.k)(n)&&(eD(t)||iw(t))?t:void 0,c=n;u&&!i&&(i=[eD(u)?u.__ref:u]);var l=function(e,t){return(0,tP.k)(e)?"number"==typeof t?e[t]:void 0:r.store.getFieldValue(e,String(t))};e.map.forEach(function(e,t){var n=l(u,t),o=l(c,t);if(void 0!==o){i&&i.push(t);var f=a.applyMerges(e,n,o,r,i);f!==o&&(s=s||new Map).set(t,f),i&&(0,Q.kG)(i.pop()===t)}}),s&&(n=(0,tP.k)(c)?c.slice(0):(0,en.pi)({},c),s.forEach(function(e,t){n[t]=e}))}return e.info?this.cache.policies.runMergeFunction(t,n,e.info,r,i&&(o=r.store).getStorage.apply(o,i)):n},e}(),i6=[];function i5(e,t){var n=e.map;return n.has(t)||n.set(t,i6.pop()||{map:new Map}),n.get(t)}function i8(e,t){if(e===t||!t||i9(t))return e;if(!e||i9(e))return t;var n=e.info&&t.info?(0,en.pi)((0,en.pi)({},e.info),t.info):e.info||t.info,r=e.map.size&&t.map.size,i=r?new Map:e.map.size?e.map:t.map,a={info:n,map:i};if(r){var o=new Set(t.map.keys());e.map.forEach(function(e,n){a.map.set(n,i8(e,t.map.get(n))),o.delete(n)}),o.forEach(function(n){a.map.set(n,i8(t.map.get(n),e.map.get(n)))})}return a}function i9(e){return!e||!(e.info||e.map.size)}function i7(e,t){var n=e.map,r=n.get(t);r&&i9(r)&&(i6.push(r),n.delete(t))}var ae=new Set;function at(e,t,n,r){var i=function(e){var t=r.getFieldValue(e,n);return"object"==typeof t&&t},a=i(e);if(a){var o=i(t);if(!(!o||eD(a)||(0,nm.D)(a,o)||Object.keys(a).every(function(e){return void 0!==r.getFieldValue(o,e)}))){var s=r.getFieldValue(e,"__typename")||r.getFieldValue(t,"__typename"),u=iv(n),c="".concat(s,".").concat(u);if(!ae.has(c)){ae.add(c);var l=[];(0,tP.k)(a)||(0,tP.k)(o)||[a,o].forEach(function(e){var t=r.getFieldValue(e,"__typename");"string"!=typeof t||l.includes(t)||l.push(t)}),__DEV__&&Q.kG.warn("Cache data may be lost when replacing the ".concat(u," field of a ").concat(s," object.\n\nThis could cause additional (usually avoidable) network requests to fetch data that were otherwise cached.\n\nTo address this problem (which is not a bug in Apollo Client), ").concat(l.length?"either ensure all objects of type "+l.join(" and ")+" have an ID or a custom merge function, or ":"","define a custom merge function for the ").concat(c," field, so InMemoryCache can safely merge these objects:\n\n existing: ").concat(JSON.stringify(a).slice(0,1e3),"\n incoming: ").concat(JSON.stringify(o).slice(0,1e3),"\n\nFor more information about these options, please refer to the documentation:\n\n * Ensuring entity objects have IDs: https://go.apollo.dev/c/generating-unique-identifiers\n * Defining custom merge functions: https://go.apollo.dev/c/merging-non-normalized-objects\n"))}}}}var an=function(e){function t(t){void 0===t&&(t={});var n=e.call(this)||this;return n.watches=new Set,n.typenameDocumentCache=new Map,n.makeVar=r2,n.txCount=0,n.config=ip(t),n.addTypename=!!n.config.addTypename,n.policies=new iQ({cache:n,dataIdFromObject:n.config.dataIdFromObject,possibleTypes:n.config.possibleTypes,typePolicies:n.config.typePolicies}),n.init(),n}return(0,en.ZT)(t,e),t.prototype.init=function(){var e=this.data=new iT.Root({policies:this.policies,resultCaching:this.config.resultCaching});this.optimisticData=e.stump,this.resetResultCache()},t.prototype.resetResultCache=function(e){var t=this,n=this.storeReader,r=this.config.fragments;this.storeWriter=new i4(this,this.storeReader=new iP({cache:this,addTypename:this.addTypename,resultCacheMaxSize:this.config.resultCacheMaxSize,canonizeResults:ib(this.config),canon:e?void 0:n&&n.canon,fragments:r}),r),this.maybeBroadcastWatch=rZ(function(e,n){return t.broadcastWatch(e,n)},{max:this.config.resultCacheMaxSize,makeCacheKey:function(e){var n=e.optimistic?t.optimisticData:t.data;if(iD(n)){var r=e.optimistic,i=e.id,a=e.variables;return n.makeCacheKey(e.query,e.callback,nx({optimistic:r,id:i,variables:a}))}}}),new Set([this.data.group,this.optimisticData.group,]).forEach(function(e){return e.resetCaching()})},t.prototype.restore=function(e){return this.init(),e&&this.data.replace(e),this},t.prototype.extract=function(e){return void 0===e&&(e=!1),(e?this.optimisticData:this.data).extract()},t.prototype.read=function(e){var t=e.returnPartialData,n=void 0!==t&&t;try{return this.storeReader.diffQueryAgainstStore((0,en.pi)((0,en.pi)({},e),{store:e.optimistic?this.optimisticData:this.data,config:this.config,returnPartialData:n})).result||null}catch(r){if(r instanceof is)return null;throw r}},t.prototype.write=function(e){try{return++this.txCount,this.storeWriter.writeToStore(this.data,e)}finally{--this.txCount||!1===e.broadcast||this.broadcastWatches()}},t.prototype.modify=function(e){if(ic.call(e,"id")&&!e.id)return!1;var t=e.optimistic?this.optimisticData:this.data;try{return++this.txCount,t.modify(e.id||"ROOT_QUERY",e.fields)}finally{--this.txCount||!1===e.broadcast||this.broadcastWatches()}},t.prototype.diff=function(e){return this.storeReader.diffQueryAgainstStore((0,en.pi)((0,en.pi)({},e),{store:e.optimistic?this.optimisticData:this.data,rootId:e.id||"ROOT_QUERY",config:this.config}))},t.prototype.watch=function(e){var t=this;return this.watches.size||r0(this),this.watches.add(e),e.immediate&&this.maybeBroadcastWatch(e),function(){t.watches.delete(e)&&!t.watches.size&&r1(t),t.maybeBroadcastWatch.forget(e)}},t.prototype.gc=function(e){nx.reset();var t=this.optimisticData.gc();return e&&!this.txCount&&(e.resetResultCache?this.resetResultCache(e.resetResultIdentities):e.resetResultIdentities&&this.storeReader.resetCanon()),t},t.prototype.retain=function(e,t){return(t?this.optimisticData:this.data).retain(e)},t.prototype.release=function(e,t){return(t?this.optimisticData:this.data).release(e)},t.prototype.identify=function(e){if(eD(e))return e.__ref;try{return this.policies.identify(e)[0]}catch(t){__DEV__&&Q.kG.warn(t)}},t.prototype.evict=function(e){if(!e.id){if(ic.call(e,"id"))return!1;e=(0,en.pi)((0,en.pi)({},e),{id:"ROOT_QUERY"})}try{return++this.txCount,this.optimisticData.evict(e,this.data)}finally{--this.txCount||!1===e.broadcast||this.broadcastWatches()}},t.prototype.reset=function(e){var t=this;return this.init(),nx.reset(),e&&e.discardWatches?(this.watches.forEach(function(e){return t.maybeBroadcastWatch.forget(e)}),this.watches.clear(),r1(this)):this.broadcastWatches(),Promise.resolve()},t.prototype.removeOptimistic=function(e){var t=this.optimisticData.removeLayer(e);t!==this.optimisticData&&(this.optimisticData=t,this.broadcastWatches())},t.prototype.batch=function(e){var t,n=this,r=e.update,i=e.optimistic,a=void 0===i||i,o=e.removeOptimistic,s=e.onWatchUpdated,u=function(e){var i=n,a=i.data,o=i.optimisticData;++n.txCount,e&&(n.data=n.optimisticData=e);try{return t=r(n)}finally{--n.txCount,n.data=a,n.optimisticData=o}},c=new Set;return s&&!this.txCount&&this.broadcastWatches((0,en.pi)((0,en.pi)({},e),{onWatchUpdated:function(e){return c.add(e),!1}})),"string"==typeof a?this.optimisticData=this.optimisticData.addLayer(a,u):!1===a?u(this.data):u(),"string"==typeof o&&(this.optimisticData=this.optimisticData.removeLayer(o)),s&&c.size?(this.broadcastWatches((0,en.pi)((0,en.pi)({},e),{onWatchUpdated:function(e,t){var n=s.call(this,e,t);return!1!==n&&c.delete(e),n}})),c.size&&c.forEach(function(e){return n.maybeBroadcastWatch.dirty(e)})):this.broadcastWatches(e),t},t.prototype.performTransaction=function(e,t){return this.batch({update:e,optimistic:t||null!==t})},t.prototype.transformDocument=function(e){if(this.addTypename){var t=this.typenameDocumentCache.get(e);return t||(t=nj(e),this.typenameDocumentCache.set(e,t),this.typenameDocumentCache.set(t,t)),t}return e},t.prototype.transformForLink=function(e){var t=this.config.fragments;return t?t.transform(e):e},t.prototype.broadcastWatches=function(e){var t=this;this.txCount||this.watches.forEach(function(n){return t.maybeBroadcastWatch(n,e)})},t.prototype.broadcastWatch=function(e,t){var n=e.lastDiff,r=this.diff(e);(!t||(e.optimistic&&"string"==typeof t.optimistic&&(r.fromOptimisticTransaction=!0),!t.onWatchUpdated||!1!==t.onWatchUpdated.call(this,e,r,n)))&&(n&&(0,nm.D)(n.result,r.result)||e.callback(e.lastDiff=r,n))},t}(io),ar={possibleTypes:{ApproveJobProposalSpecPayload:["ApproveJobProposalSpecSuccess","JobAlreadyExistsError","NotFoundError"],BridgePayload:["Bridge","NotFoundError"],CancelJobProposalSpecPayload:["CancelJobProposalSpecSuccess","NotFoundError"],ChainPayload:["Chain","NotFoundError"],CreateAPITokenPayload:["CreateAPITokenSuccess","InputErrors"],CreateBridgePayload:["CreateBridgeSuccess"],CreateCSAKeyPayload:["CSAKeyExistsError","CreateCSAKeySuccess"],CreateFeedsManagerChainConfigPayload:["CreateFeedsManagerChainConfigSuccess","InputErrors","NotFoundError"],CreateFeedsManagerPayload:["CreateFeedsManagerSuccess","DuplicateFeedsManagerError","InputErrors","NotFoundError","SingleFeedsManagerError"],CreateJobPayload:["CreateJobSuccess","InputErrors"],CreateOCR2KeyBundlePayload:["CreateOCR2KeyBundleSuccess"],CreateOCRKeyBundlePayload:["CreateOCRKeyBundleSuccess"],CreateP2PKeyPayload:["CreateP2PKeySuccess"],DeleteAPITokenPayload:["DeleteAPITokenSuccess","InputErrors"],DeleteBridgePayload:["DeleteBridgeConflictError","DeleteBridgeInvalidNameError","DeleteBridgeSuccess","NotFoundError"],DeleteCSAKeyPayload:["DeleteCSAKeySuccess","NotFoundError"],DeleteFeedsManagerChainConfigPayload:["DeleteFeedsManagerChainConfigSuccess","NotFoundError"],DeleteJobPayload:["DeleteJobSuccess","NotFoundError"],DeleteOCR2KeyBundlePayload:["DeleteOCR2KeyBundleSuccess","NotFoundError"],DeleteOCRKeyBundlePayload:["DeleteOCRKeyBundleSuccess","NotFoundError"],DeleteP2PKeyPayload:["DeleteP2PKeySuccess","NotFoundError"],DeleteVRFKeyPayload:["DeleteVRFKeySuccess","NotFoundError"],DismissJobErrorPayload:["DismissJobErrorSuccess","NotFoundError"],Error:["CSAKeyExistsError","DeleteBridgeConflictError","DeleteBridgeInvalidNameError","DuplicateFeedsManagerError","InputError","JobAlreadyExistsError","NotFoundError","RunJobCannotRunError","SingleFeedsManagerError"],EthTransactionPayload:["EthTransaction","NotFoundError"],FeaturesPayload:["Features"],FeedsManagerPayload:["FeedsManager","NotFoundError"],GetSQLLoggingPayload:["SQLLogging"],GlobalLogLevelPayload:["GlobalLogLevel"],JobPayload:["Job","NotFoundError"],JobProposalPayload:["JobProposal","NotFoundError"],JobRunPayload:["JobRun","NotFoundError"],JobSpec:["BlockHeaderFeederSpec","BlockhashStoreSpec","BootstrapSpec","CronSpec","DirectRequestSpec","FluxMonitorSpec","GatewaySpec","KeeperSpec","OCR2Spec","OCRSpec","StandardCapabilitiesSpec","VRFSpec","WebhookSpec","WorkflowSpec"],NodePayload:["Node","NotFoundError"],PaginatedPayload:["BridgesPayload","ChainsPayload","EthTransactionAttemptsPayload","EthTransactionsPayload","JobRunsPayload","JobsPayload","NodesPayload"],RejectJobProposalSpecPayload:["NotFoundError","RejectJobProposalSpecSuccess"],RunJobPayload:["NotFoundError","RunJobCannotRunError","RunJobSuccess"],SetGlobalLogLevelPayload:["InputErrors","SetGlobalLogLevelSuccess"],SetSQLLoggingPayload:["SetSQLLoggingSuccess"],SetServicesLogLevelsPayload:["InputErrors","SetServicesLogLevelsSuccess"],UpdateBridgePayload:["NotFoundError","UpdateBridgeSuccess"],UpdateFeedsManagerChainConfigPayload:["InputErrors","NotFoundError","UpdateFeedsManagerChainConfigSuccess"],UpdateFeedsManagerPayload:["InputErrors","NotFoundError","UpdateFeedsManagerSuccess"],UpdateJobProposalSpecDefinitionPayload:["NotFoundError","UpdateJobProposalSpecDefinitionSuccess"],UpdatePasswordPayload:["InputErrors","UpdatePasswordSuccess"],VRFKeyPayload:["NotFoundError","VRFKeySuccess"]}};let ai=ar;var aa=(r=void 0,location.origin),ao=new nh({uri:"".concat(aa,"/query"),credentials:"include"}),as=new ia({cache:new an({possibleTypes:ai.possibleTypes}),link:ao});if(a.Z.locale(o),u().defaultFormat="YYYY-MM-DD h:mm:ss A","undefined"!=typeof document){var au,ac,al=f().hydrate;ac=X,al(c.createElement(et,{client:as},c.createElement(d.zj,null,c.createElement(i.MuiThemeProvider,{theme:J.r},c.createElement(ac,null)))),document.getElementById("root"))}})()})(); \ No newline at end of file +`+(a!==i?`result of cast: ${a}`:""))}return r}_cast(e,t){let n=void 0===e?e:this.transforms.reduce((t,n)=>n.call(this,t,e,this),e);return void 0===n&&(n=this.getDefault()),n}_validate(e,t={},n){let{sync:r,path:i,from:a=[],originalValue:o=e,strict:s=this.spec.strict,abortEarly:u=this.spec.abortEarly}=t,c=e;s||(c=this._cast(c,pB({assert:!1},t)));let l={value:c,path:i,options:t,originalValue:o,schema:this,label:this.spec.label,sync:r,from:a},f=[];this._typeError&&f.push(this._typeError),this._whitelistError&&f.push(this._whitelistError),this._blacklistError&&f.push(this._blacklistError),pO({args:l,value:c,path:i,sync:r,tests:f,endEarly:u},e=>{if(e)return void n(e,c);pO({tests:this.tests,args:l,path:i,sync:r,value:c,endEarly:u},n)})}validate(e,t,n){let r=this.resolve(pB({},t,{value:e}));return"function"==typeof n?r._validate(e,t,n):new Promise((n,i)=>r._validate(e,t,(e,t)=>{e?i(e):n(t)}))}validateSync(e,t){let n;return this.resolve(pB({},t,{value:e}))._validate(e,pB({},t,{sync:!0}),(e,t)=>{if(e)throw e;n=t}),n}isValid(e,t){return this.validate(e,t).then(()=>!0,e=>{if(pT.isError(e))return!1;throw e})}isValidSync(e,t){try{return this.validateSync(e,t),!0}catch(n){if(pT.isError(n))return!1;throw n}}_getDefault(){let e=this.spec.default;return null==e?e:"function"==typeof e?e.call(this):pn(e)}getDefault(e){return this.resolve(e||{})._getDefault()}default(e){return 0===arguments.length?this._getDefault():this.clone({default:e})}strict(e=!0){var t=this.clone();return t.spec.strict=e,t}_isPresent(e){return null!=e}defined(e=pf.defined){return this.test({message:e,name:"defined",exclusive:!0,test:e=>void 0!==e})}required(e=pf.required){return this.clone({presence:"required"}).withMutation(t=>t.test({message:e,name:"required",exclusive:!0,test(e){return this.schema._isPresent(e)}}))}notRequired(){var e=this.clone({presence:"optional"});return e.tests=e.tests.filter(e=>"required"!==e.OPTIONS.name),e}nullable(e=!0){return this.clone({nullable:!1!==e})}transform(e){var t=this.clone();return t.transforms.push(e),t}test(...e){let t;if(void 0===(t=1===e.length?"function"==typeof e[0]?{test:e[0]}:e[0]:2===e.length?{name:e[0],test:e[1]}:{name:e[0],message:e[1],test:e[2]}).message&&(t.message=pf.default),"function"!=typeof t.test)throw TypeError("`test` is a required parameters");let n=this.clone(),r=pR(t),i=t.exclusive||t.name&&!0===n.exclusiveTests[t.name];if(t.exclusive&&!t.name)throw TypeError("Exclusive tests must provide a unique `name` identifying the test");return t.name&&(n.exclusiveTests[t.name]=!!t.exclusive),n.tests=n.tests.filter(e=>e.OPTIONS.name!==t.name||!i&&e.OPTIONS.test!==r.OPTIONS.test),n.tests.push(r),n}when(e,t){Array.isArray(e)||"string"==typeof e||(t=e,e=".");let n=this.clone(),r=pS(e).map(e=>new pD(e));return r.forEach(e=>{e.isSibling&&n.deps.push(e.key)}),n.conditions.push(new pE(r,t)),n}typeError(e){var t=this.clone();return t._typeError=pR({message:e,name:"typeError",test(e){return!!(void 0===e||this.schema.isType(e))||this.createError({params:{type:this.schema._type}})}}),t}oneOf(e,t=pf.oneOf){var n=this.clone();return e.forEach(e=>{n._whitelist.add(e),n._blacklist.delete(e)}),n._whitelistError=pR({message:t,name:"oneOf",test(e){if(void 0===e)return!0;let t=this.schema._whitelist;return!!t.has(e,this.resolve)||this.createError({params:{values:t.toArray().join(", ")}})}}),n}notOneOf(e,t=pf.notOneOf){var n=this.clone();return e.forEach(e=>{n._blacklist.add(e),n._whitelist.delete(e)}),n._blacklistError=pR({message:t,name:"notOneOf",test(e){let t=this.schema._blacklist;return!t.has(e,this.resolve)||this.createError({params:{values:t.toArray().join(", ")}})}}),n}strip(e=!0){let t=this.clone();return t.spec.strip=e,t}describe(){let e=this.clone(),{label:t,meta:n}=e.spec,r={meta:n,label:t,type:e.type,oneOf:e._whitelist.describe(),notOneOf:e._blacklist.describe(),tests:e.tests.map(e=>({name:e.OPTIONS.name,params:e.OPTIONS.params})).filter((e,t,n)=>n.findIndex(t=>t.name===e.name)===t)};return r}}for(let pH of(pU.prototype.__isYupSchema__=!0,["validate","validateSync"]))pU.prototype[`${pH}At`]=function(e,t,n={}){let{parent:r,parentPath:i,schema:a}=pF(this,e,t,n.context);return a[pH](r&&r[i],pB({},n,{parent:r,path:e}))};for(let p$ of["equals","is"])pU.prototype[p$]=pU.prototype.oneOf;for(let pz of["not","nope"])pU.prototype[pz]=pU.prototype.notOneOf;pU.prototype.optional=pU.prototype.notRequired;let pG=pU;function pW(){return new pG}pW.prototype=pG.prototype;let pK=e=>null==e;function pV(){return new pq}class pq extends pU{constructor(){super({type:"boolean"}),this.withMutation(()=>{this.transform(function(e){if(!this.isType(e)){if(/^(true|1)$/i.test(String(e)))return!0;if(/^(false|0)$/i.test(String(e)))return!1}return e})})}_typeCheck(e){return e instanceof Boolean&&(e=e.valueOf()),"boolean"==typeof e}isTrue(e=pb.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"true"},test:e=>pK(e)||!0===e})}isFalse(e=pb.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"false"},test:e=>pK(e)||!1===e})}}pV.prototype=pq.prototype;let pZ=/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i,pX=/^((https?|ftp):)?\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i,pJ=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i,pQ=e=>pK(e)||e===e.trim(),p1=({}).toString();function p0(){return new p2}class p2 extends pU{constructor(){super({type:"string"}),this.withMutation(()=>{this.transform(function(e){if(this.isType(e)||Array.isArray(e))return e;let t=null!=e&&e.toString?e.toString():e;return t===p1?e:t})})}_typeCheck(e){return e instanceof String&&(e=e.valueOf()),"string"==typeof e}_isPresent(e){return super._isPresent(e)&&!!e.length}length(e,t=pd.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pK(t)||t.length===this.resolve(e)}})}min(e,t=pd.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pK(t)||t.length>=this.resolve(e)}})}max(e,t=pd.max){return this.test({name:"max",exclusive:!0,message:t,params:{max:e},test(t){return pK(t)||t.length<=this.resolve(e)}})}matches(e,t){let n=!1,r,i;return t&&("object"==typeof t?{excludeEmptyString:n=!1,message:r,name:i}=t:r=t),this.test({name:i||"matches",message:r||pd.matches,params:{regex:e},test:t=>pK(t)||""===t&&n||-1!==t.search(e)})}email(e=pd.email){return this.matches(pZ,{name:"email",message:e,excludeEmptyString:!0})}url(e=pd.url){return this.matches(pX,{name:"url",message:e,excludeEmptyString:!0})}uuid(e=pd.uuid){return this.matches(pJ,{name:"uuid",message:e,excludeEmptyString:!1})}ensure(){return this.default("").transform(e=>null===e?"":e)}trim(e=pd.trim){return this.transform(e=>null!=e?e.trim():e).test({message:e,name:"trim",test:pQ})}lowercase(e=pd.lowercase){return this.transform(e=>pK(e)?e:e.toLowerCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pK(e)||e===e.toLowerCase()})}uppercase(e=pd.uppercase){return this.transform(e=>pK(e)?e:e.toUpperCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pK(e)||e===e.toUpperCase()})}}p0.prototype=p2.prototype;let p3=e=>e!=+e;function p4(){return new p6}class p6 extends pU{constructor(){super({type:"number"}),this.withMutation(()=>{this.transform(function(e){let t=e;if("string"==typeof t){if(""===(t=t.replace(/\s/g,"")))return NaN;t=+t}return this.isType(t)?t:parseFloat(t)})})}_typeCheck(e){return e instanceof Number&&(e=e.valueOf()),"number"==typeof e&&!p3(e)}min(e,t=ph.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pK(t)||t>=this.resolve(e)}})}max(e,t=ph.max){return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pK(t)||t<=this.resolve(e)}})}lessThan(e,t=ph.lessThan){return this.test({message:t,name:"max",exclusive:!0,params:{less:e},test(t){return pK(t)||tthis.resolve(e)}})}positive(e=ph.positive){return this.moreThan(0,e)}negative(e=ph.negative){return this.lessThan(0,e)}integer(e=ph.integer){return this.test({name:"integer",message:e,test:e=>pK(e)||Number.isInteger(e)})}truncate(){return this.transform(e=>pK(e)?e:0|e)}round(e){var t,n=["ceil","floor","round","trunc"];if("trunc"===(e=(null==(t=e)?void 0:t.toLowerCase())||"round"))return this.truncate();if(-1===n.indexOf(e.toLowerCase()))throw TypeError("Only valid options for round() are: "+n.join(", "));return this.transform(t=>pK(t)?t:Math[e](t))}}p4.prototype=p6.prototype;var p5=/^(\d{4}|[+\-]\d{6})(?:-?(\d{2})(?:-?(\d{2}))?)?(?:[ T]?(\d{2}):?(\d{2})(?::?(\d{2})(?:[,\.](\d{1,}))?)?(?:(Z)|([+\-])(\d{2})(?::?(\d{2}))?)?)?$/;function p8(e){var t,n,r=[1,4,5,6,7,10,11],i=0;if(n=p5.exec(e)){for(var a,o=0;a=r[o];++o)n[a]=+n[a]||0;n[2]=(+n[2]||1)-1,n[3]=+n[3]||1,n[7]=n[7]?String(n[7]).substr(0,3):0,(void 0===n[8]||""===n[8])&&(void 0===n[9]||""===n[9])?t=+new Date(n[1],n[2],n[3],n[4],n[5],n[6],n[7]):("Z"!==n[8]&&void 0!==n[9]&&(i=60*n[10]+n[11],"+"===n[9]&&(i=0-i)),t=Date.UTC(n[1],n[2],n[3],n[4],n[5]+i,n[6],n[7]))}else t=Date.parse?Date.parse(e):NaN;return t}let p9=new Date(""),p7=e=>"[object Date]"===Object.prototype.toString.call(e);function be(){return new bt}class bt extends pU{constructor(){super({type:"date"}),this.withMutation(()=>{this.transform(function(e){return this.isType(e)?e:(e=p8(e),isNaN(e)?p9:new Date(e))})})}_typeCheck(e){return p7(e)&&!isNaN(e.getTime())}prepareParam(e,t){let n;if(pD.isRef(e))n=e;else{let r=this.cast(e);if(!this._typeCheck(r))throw TypeError(`\`${t}\` must be a Date or a value that can be \`cast()\` to a Date`);n=r}return n}min(e,t=pp.min){let n=this.prepareParam(e,"min");return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(e){return pK(e)||e>=this.resolve(n)}})}max(e,t=pp.max){var n=this.prepareParam(e,"max");return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(e){return pK(e)||e<=this.resolve(n)}})}}bt.INVALID_DATE=p9,be.prototype=bt.prototype,be.INVALID_DATE=p9;var bn=n(11865),br=n.n(bn),bi=n(68929),ba=n.n(bi),bo=n(67523),bs=n.n(bo),bu=n(94633),bc=n.n(bu);function bl(e,t=[]){let n=[],r=[];function i(e,i){var a=(0,pC.split)(e)[0];~r.indexOf(a)||r.push(a),~t.indexOf(`${i}-${a}`)||n.push([i,a])}for(let a in e)if(py()(e,a)){let o=e[a];~r.indexOf(a)||r.push(a),pD.isRef(o)&&o.isSibling?i(o.path,a):pw(o)&&"deps"in o&&o.deps.forEach(e=>i(e,a))}return bc().array(r,n).reverse()}function bf(e,t){let n=1/0;return e.some((e,r)=>{var i;if((null==(i=t.path)?void 0:i.indexOf(e))!==-1)return n=r,!0}),n}function bd(e){return(t,n)=>bf(e,t)-bf(e,n)}function bh(){return(bh=Object.assign||function(e){for(var t=1;t"[object Object]"===Object.prototype.toString.call(e);function bb(e,t){let n=Object.keys(e.fields);return Object.keys(t).filter(e=>-1===n.indexOf(e))}let bm=bd([]);class bg extends pU{constructor(e){super({type:"object"}),this.fields=Object.create(null),this._sortErrors=bm,this._nodes=[],this._excludedEdges=[],this.withMutation(()=>{this.transform(function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(t){e=null}return this.isType(e)?e:null}),e&&this.shape(e)})}_typeCheck(e){return bp(e)||"function"==typeof e}_cast(e,t={}){var n;let r=super._cast(e,t);if(void 0===r)return this.getDefault();if(!this._typeCheck(r))return r;let i=this.fields,a=null!=(n=t.stripUnknown)?n:this.spec.noUnknown,o=this._nodes.concat(Object.keys(r).filter(e=>-1===this._nodes.indexOf(e))),s={},u=bh({},t,{parent:s,__validating:t.__validating||!1}),c=!1;for(let l of o){let f=i[l],d=py()(r,l);if(f){let h,p=r[l];u.path=(t.path?`${t.path}.`:"")+l;let b="spec"in(f=f.resolve({value:p,context:t.context,parent:s}))?f.spec:void 0,m=null==b?void 0:b.strict;if(null==b?void 0:b.strip){c=c||l in r;continue}void 0!==(h=t.__validating&&m?r[l]:f.cast(r[l],u))&&(s[l]=h)}else d&&!a&&(s[l]=r[l]);s[l]!==r[l]&&(c=!0)}return c?s:r}_validate(e,t={},n){let r=[],{sync:i,from:a=[],originalValue:o=e,abortEarly:s=this.spec.abortEarly,recursive:u=this.spec.recursive}=t;a=[{schema:this,value:o},...a],t.__validating=!0,t.originalValue=o,t.from=a,super._validate(e,t,(e,c)=>{if(e){if(!pT.isError(e)||s)return void n(e,c);r.push(e)}if(!u||!bp(c)){n(r[0]||null,c);return}o=o||c;let l=this._nodes.map(e=>(n,r)=>{let i=-1===e.indexOf(".")?(t.path?`${t.path}.`:"")+e:`${t.path||""}["${e}"]`,s=this.fields[e];if(s&&"validate"in s){s.validate(c[e],bh({},t,{path:i,from:a,strict:!0,parent:c,originalValue:o[e]}),r);return}r(null)});pO({sync:i,tests:l,value:c,errors:r,endEarly:s,sort:this._sortErrors,path:t.path},n)})}clone(e){let t=super.clone(e);return t.fields=bh({},this.fields),t._nodes=this._nodes,t._excludedEdges=this._excludedEdges,t._sortErrors=this._sortErrors,t}concat(e){let t=super.concat(e),n=t.fields;for(let[r,i]of Object.entries(this.fields)){let a=n[r];void 0===a?n[r]=i:a instanceof pU&&i instanceof pU&&(n[r]=i.concat(a))}return t.withMutation(()=>t.shape(n))}getDefaultFromShape(){let e={};return this._nodes.forEach(t=>{let n=this.fields[t];e[t]="default"in n?n.getDefault():void 0}),e}_getDefault(){return"default"in this.spec?super._getDefault():this._nodes.length?this.getDefaultFromShape():void 0}shape(e,t=[]){let n=this.clone(),r=Object.assign(n.fields,e);if(n.fields=r,n._sortErrors=bd(Object.keys(r)),t.length){Array.isArray(t[0])||(t=[t]);let i=t.map(([e,t])=>`${e}-${t}`);n._excludedEdges=n._excludedEdges.concat(i)}return n._nodes=bl(r,n._excludedEdges),n}pick(e){let t={};for(let n of e)this.fields[n]&&(t[n]=this.fields[n]);return this.clone().withMutation(e=>(e.fields={},e.shape(t)))}omit(e){let t=this.clone(),n=t.fields;for(let r of(t.fields={},e))delete n[r];return t.withMutation(()=>t.shape(n))}from(e,t,n){let r=(0,pC.getter)(e,!0);return this.transform(i=>{if(null==i)return i;let a=i;return py()(i,e)&&(a=bh({},i),n||delete a[e],a[t]=r(i)),a})}noUnknown(e=!0,t=pm.noUnknown){"string"==typeof e&&(t=e,e=!0);let n=this.test({name:"noUnknown",exclusive:!0,message:t,test(t){if(null==t)return!0;let n=bb(this.schema,t);return!e||0===n.length||this.createError({params:{unknown:n.join(", ")}})}});return n.spec.noUnknown=e,n}unknown(e=!0,t=pm.noUnknown){return this.noUnknown(!e,t)}transformKeys(e){return this.transform(t=>t&&bs()(t,(t,n)=>e(n)))}camelCase(){return this.transformKeys(ba())}snakeCase(){return this.transformKeys(br())}constantCase(){return this.transformKeys(e=>br()(e).toUpperCase())}describe(){let e=super.describe();return e.fields=pL()(this.fields,e=>e.describe()),e}}function bv(e){return new bg(e)}function by(){return(by=Object.assign||function(e){for(var t=1;t{this.transform(function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(t){e=null}return this.isType(e)?e:null})})}_typeCheck(e){return Array.isArray(e)}get _subType(){return this.innerType}_cast(e,t){let n=super._cast(e,t);if(!this._typeCheck(n)||!this.innerType)return n;let r=!1,i=n.map((e,n)=>{let i=this.innerType.cast(e,by({},t,{path:`${t.path||""}[${n}]`}));return i!==e&&(r=!0),i});return r?i:n}_validate(e,t={},n){var r,i;let a=[],o=t.sync,s=t.path,u=this.innerType,c=null!=(r=t.abortEarly)?r:this.spec.abortEarly,l=null!=(i=t.recursive)?i:this.spec.recursive,f=null!=t.originalValue?t.originalValue:e;super._validate(e,t,(e,r)=>{if(e){if(!pT.isError(e)||c)return void n(e,r);a.push(e)}if(!l||!u||!this._typeCheck(r)){n(a[0]||null,r);return}f=f||r;let i=Array(r.length);for(let d=0;du.validate(h,b,t)}pO({sync:o,path:s,value:r,errors:a,endEarly:c,tests:i},n)})}clone(e){let t=super.clone(e);return t.innerType=this.innerType,t}concat(e){let t=super.concat(e);return t.innerType=this.innerType,e.innerType&&(t.innerType=t.innerType?t.innerType.concat(e.innerType):e.innerType),t}of(e){let t=this.clone();if(!pw(e))throw TypeError("`array.of()` sub-schema must be a valid yup schema not: "+pl(e));return t.innerType=e,t}length(e,t=pg.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pK(t)||t.length===this.resolve(e)}})}min(e,t){return t=t||pg.min,this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pK(t)||t.length>=this.resolve(e)}})}max(e,t){return t=t||pg.max,this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pK(t)||t.length<=this.resolve(e)}})}ensure(){return this.default(()=>[]).transform((e,t)=>this._typeCheck(e)?e:null==t?[]:[].concat(t))}compact(e){let t=e?(t,n,r)=>!e(t,n,r):e=>!!e;return this.transform(e=>null!=e?e.filter(t):e)}describe(){let e=super.describe();return this.innerType&&(e.innerType=this.innerType.describe()),e}nullable(e=!0){return super.nullable(e)}defined(){return super.defined()}required(e){return super.required(e)}}bw.prototype=b_.prototype;var bE=bv().shape({name:p0().required("Required"),url:p0().required("Required")}),bS=function(e){var t=e.initialValues,n=e.onSubmit,r=e.submitButtonText,i=e.nameDisabled,a=void 0!==i&&i;return l.createElement(hT,{initialValues:t,validationSchema:bE,onSubmit:n},function(e){var t=e.isSubmitting;return l.createElement(l.Fragment,null,l.createElement(hR,{"data-testid":"bridge-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(hP,{component:hX,id:"name",name:"name",label:"Name",disabled:a,required:!0,fullWidth:!0,FormHelperTextProps:{"data-testid":"name-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(hP,{component:hX,id:"url",name:"url",label:"Bridge URL",placeholder:"https://",required:!0,fullWidth:!0,FormHelperTextProps:{"data-testid":"url-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:7},l.createElement(hP,{component:hX,id:"minimumContractPayment",name:"minimumContractPayment",label:"Minimum Contract Payment",placeholder:"0",fullWidth:!0,inputProps:{min:0},FormHelperTextProps:{"data-testid":"minimumContractPayment-helper-text"}})),l.createElement(d.Z,{item:!0,xs:7},l.createElement(hP,{component:hX,id:"confirmations",name:"confirmations",label:"Confirmations",placeholder:"0",type:"number",fullWidth:!0,inputProps:{min:0},FormHelperTextProps:{"data-testid":"confirmations-helper-text"}})))),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(ok.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},r)))))})},bk=function(e){var t=e.bridge,n=e.onSubmit,r={name:t.name,url:t.url,minimumContractPayment:t.minimumContractPayment,confirmations:t.confirmations};return l.createElement(ig,null,l.createElement(d.Z,{container:!0,spacing:40},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"Edit Bridge",action:l.createElement(aA.Z,{component:tz,href:"/bridges/".concat(t.id)},"Cancel")}),l.createElement(aW.Z,null,l.createElement(bS,{nameDisabled:!0,initialValues:r,onSubmit:n,submitButtonText:"Save Bridge"}))))))};function bx(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&void 0!==arguments[0]&&arguments[0],t=e?function(){return l.createElement(x.default,{variant:"body1"},"Loading...")}:function(){return null};return{isLoading:e,LoadingPlaceholder:t}},mc=n(76023);function ml(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function mB(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n=4?[e[0],e[1],e[2],e[3],"".concat(e[0],".").concat(e[1]),"".concat(e[0],".").concat(e[2]),"".concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[0]),"".concat(e[1],".").concat(e[2]),"".concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[1]),"".concat(e[2],".").concat(e[3]),"".concat(e[3],".").concat(e[0]),"".concat(e[3],".").concat(e[1]),"".concat(e[3],".").concat(e[2]),"".concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[1],".").concat(e[3]),"".concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[2],".").concat(e[3]),"".concat(e[0],".").concat(e[3],".").concat(e[1]),"".concat(e[0],".").concat(e[3],".").concat(e[2]),"".concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[1],".").concat(e[2],".").concat(e[3]),"".concat(e[1],".").concat(e[3],".").concat(e[0]),"".concat(e[1],".").concat(e[3],".").concat(e[2]),"".concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[0],".").concat(e[3]),"".concat(e[2],".").concat(e[1],".").concat(e[0]),"".concat(e[2],".").concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[3],".").concat(e[0]),"".concat(e[2],".").concat(e[3],".").concat(e[1]),"".concat(e[3],".").concat(e[0],".").concat(e[1]),"".concat(e[3],".").concat(e[0],".").concat(e[2]),"".concat(e[3],".").concat(e[1],".").concat(e[0]),"".concat(e[3],".").concat(e[1],".").concat(e[2]),"".concat(e[3],".").concat(e[2],".").concat(e[0]),"".concat(e[3],".").concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[1],".").concat(e[2],".").concat(e[3]),"".concat(e[0],".").concat(e[1],".").concat(e[3],".").concat(e[2]),"".concat(e[0],".").concat(e[2],".").concat(e[1],".").concat(e[3]),"".concat(e[0],".").concat(e[2],".").concat(e[3],".").concat(e[1]),"".concat(e[0],".").concat(e[3],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[3],".").concat(e[2],".").concat(e[1]),"".concat(e[1],".").concat(e[0],".").concat(e[2],".").concat(e[3]),"".concat(e[1],".").concat(e[0],".").concat(e[3],".").concat(e[2]),"".concat(e[1],".").concat(e[2],".").concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[2],".").concat(e[3],".").concat(e[0]),"".concat(e[1],".").concat(e[3],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[3],".").concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[0],".").concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[0],".").concat(e[3],".").concat(e[1]),"".concat(e[2],".").concat(e[1],".").concat(e[0],".").concat(e[3]),"".concat(e[2],".").concat(e[1],".").concat(e[3],".").concat(e[0]),"".concat(e[2],".").concat(e[3],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[3],".").concat(e[1],".").concat(e[0]),"".concat(e[3],".").concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[3],".").concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[3],".").concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[3],".").concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[3],".").concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[3],".").concat(e[2],".").concat(e[1],".").concat(e[0])]:void 0}var mZ={};function mX(e){if(0===e.length||1===e.length)return e;var t=e.join(".");return mZ[t]||(mZ[t]=mq(e)),mZ[t]}function mJ(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0;return mX(e.filter(function(e){return"token"!==e})).reduce(function(e,t){return mK({},e,n[t])},t)}function mQ(e){return e.join(" ")}function m1(e,t){var n=0;return function(r){return n+=1,r.map(function(r,i){return m0({node:r,stylesheet:e,useInlineStyles:t,key:"code-segment-".concat(n,"-").concat(i)})})}}function m0(e){var t=e.node,n=e.stylesheet,r=e.style,i=void 0===r?{}:r,a=e.useInlineStyles,o=e.key,s=t.properties,u=t.type,c=t.tagName,f=t.value;if("text"===u)return f;if(c){var d,h=m1(n,a);if(a){var p=Object.keys(n).reduce(function(e,t){return t.split(".").forEach(function(t){e.includes(t)||e.push(t)}),e},[]),b=s.className&&s.className.includes("token")?["token"]:[],m=s.className&&b.concat(s.className.filter(function(e){return!p.includes(e)}));d=mK({},s,{className:mQ(m)||void 0,style:mJ(s.className,Object.assign({},s.style,i),n)})}else d=mK({},s,{className:mQ(s.className)});var g=h(t.children);return l.createElement(c,(0,mV.Z)({key:o},d),g)}}let m2=function(e,t){return -1!==e.listLanguages().indexOf(t)};var m3=/\n/g;function m4(e){return e.match(m3)}function m6(e){var t=e.lines,n=e.startingLineNumber,r=e.style;return t.map(function(e,t){var i=t+n;return l.createElement("span",{key:"line-".concat(t),className:"react-syntax-highlighter-line-number",style:"function"==typeof r?r(i):r},"".concat(i,"\n"))})}function m5(e){var t=e.codeString,n=e.codeStyle,r=e.containerStyle,i=void 0===r?{float:"left",paddingRight:"10px"}:r,a=e.numberStyle,o=void 0===a?{}:a,s=e.startingLineNumber;return l.createElement("code",{style:Object.assign({},n,i)},m6({lines:t.replace(/\n$/,"").split("\n"),style:o,startingLineNumber:s}))}function m8(e){return"".concat(e.toString().length,".25em")}function m9(e,t){return{type:"element",tagName:"span",properties:{key:"line-number--".concat(e),className:["comment","linenumber","react-syntax-highlighter-line-number"],style:t},children:[{type:"text",value:e}]}}function m7(e,t,n){var r,i={display:"inline-block",minWidth:m8(n),paddingRight:"1em",textAlign:"right",userSelect:"none"};return mK({},i,"function"==typeof e?e(t):e)}function ge(e){var t=e.children,n=e.lineNumber,r=e.lineNumberStyle,i=e.largestLineNumber,a=e.showInlineLineNumbers,o=e.lineProps,s=void 0===o?{}:o,u=e.className,c=void 0===u?[]:u,l=e.showLineNumbers,f=e.wrapLongLines,d="function"==typeof s?s(n):s;if(d.className=c,n&&a){var h=m7(r,n,i);t.unshift(m9(n,h))}return f&l&&(d.style=mK({},d.style,{display:"flex"})),{type:"element",tagName:"span",properties:d,children:t}}function gt(e){for(var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],r=0;r2&&void 0!==arguments[2]?arguments[2]:[];return ge({children:e,lineNumber:t,lineNumberStyle:s,largestLineNumber:o,showInlineLineNumbers:i,lineProps:n,className:a,showLineNumbers:r,wrapLongLines:u})}function b(e,t){if(r&&t&&i){var n=m7(s,t,o);e.unshift(m9(t,n))}return e}function m(e,n){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[];return t||r.length>0?p(e,n,r):b(e,n)}for(var g=function(){var e=l[h],t=e.children[0].value;if(m4(t)){var n=t.split("\n");n.forEach(function(t,i){var o=r&&f.length+a,s={type:"text",value:"".concat(t,"\n")};if(0===i){var u=l.slice(d+1,h).concat(ge({children:[s],className:e.properties.className})),c=m(u,o);f.push(c)}else if(i===n.length-1){if(l[h+1]&&l[h+1].children&&l[h+1].children[0]){var p={type:"text",value:"".concat(t)},b=ge({children:[p],className:e.properties.className});l.splice(h+1,0,b)}else{var g=[s],v=m(g,o,e.properties.className);f.push(v)}}else{var y=[s],w=m(y,o,e.properties.className);f.push(w)}}),d=h}h++};h code[class*="language-"]':{background:"#f5f2f0",padding:".1em",borderRadius:".3em",whiteSpace:"normal"},comment:{color:"slategray"},prolog:{color:"slategray"},doctype:{color:"slategray"},cdata:{color:"slategray"},punctuation:{color:"#999"},namespace:{Opacity:".7"},property:{color:"#905"},tag:{color:"#905"},boolean:{color:"#905"},number:{color:"#905"},constant:{color:"#905"},symbol:{color:"#905"},deleted:{color:"#905"},selector:{color:"#690"},"attr-name":{color:"#690"},string:{color:"#690"},char:{color:"#690"},builtin:{color:"#690"},inserted:{color:"#690"},operator:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},entity:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)",cursor:"help"},url:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},".language-css .token.string":{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},".style .token.string":{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},atrule:{color:"#07a"},"attr-value":{color:"#07a"},keyword:{color:"#07a"},function:{color:"#DD4A68"},"class-name":{color:"#DD4A68"},regex:{color:"#e90"},important:{color:"#e90",fontWeight:"bold"},variable:{color:"#e90"},bold:{fontWeight:"bold"},italic:{fontStyle:"italic"}};var gu=n(98695),gc=n.n(gu);let gl=["abap","abnf","actionscript","ada","agda","al","antlr4","apacheconf","apl","applescript","aql","arduino","arff","asciidoc","asm6502","aspnet","autohotkey","autoit","bash","basic","batch","bbcode","birb","bison","bnf","brainfuck","brightscript","bro","bsl","c","cil","clike","clojure","cmake","coffeescript","concurnas","cpp","crystal","csharp","csp","css-extras","css","cypher","d","dart","dax","dhall","diff","django","dns-zone-file","docker","ebnf","editorconfig","eiffel","ejs","elixir","elm","erb","erlang","etlua","excel-formula","factor","firestore-security-rules","flow","fortran","fsharp","ftl","gcode","gdscript","gedcom","gherkin","git","glsl","gml","go","graphql","groovy","haml","handlebars","haskell","haxe","hcl","hlsl","hpkp","hsts","http","ichigojam","icon","iecst","ignore","inform7","ini","io","j","java","javadoc","javadoclike","javascript","javastacktrace","jolie","jq","js-extras","js-templates","jsdoc","json","json5","jsonp","jsstacktrace","jsx","julia","keyman","kotlin","latex","latte","less","lilypond","liquid","lisp","livescript","llvm","lolcode","lua","makefile","markdown","markup-templating","markup","matlab","mel","mizar","mongodb","monkey","moonscript","n1ql","n4js","nand2tetris-hdl","naniscript","nasm","neon","nginx","nim","nix","nsis","objectivec","ocaml","opencl","oz","parigp","parser","pascal","pascaligo","pcaxis","peoplecode","perl","php-extras","php","phpdoc","plsql","powerquery","powershell","processing","prolog","properties","protobuf","pug","puppet","pure","purebasic","purescript","python","q","qml","qore","r","racket","reason","regex","renpy","rest","rip","roboconf","robotframework","ruby","rust","sas","sass","scala","scheme","scss","shell-session","smali","smalltalk","smarty","sml","solidity","solution-file","soy","sparql","splunk-spl","sqf","sql","stan","stylus","swift","t4-cs","t4-templating","t4-vb","tap","tcl","textile","toml","tsx","tt2","turtle","twig","typescript","typoscript","unrealscript","vala","vbnet","velocity","verilog","vhdl","vim","visual-basic","warpscript","wasm","wiki","xeora","xml-doc","xojo","xquery","yaml","yang","zig"];var gf=go(gc(),gs);gf.supportedLanguages=gl;let gd=gf;var gh=n(64566);function gp(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function gb(){var e=gp(["\n query FetchConfigV2 {\n configv2 {\n user\n effective\n }\n }\n"]);return gb=function(){return e},e}var gm=n0(gb()),gg=function(e){var t=e.children;return l.createElement(ir.Z,null,l.createElement(r7.default,{component:"th",scope:"row",colSpan:3},t))},gv=function(){return l.createElement(gg,null,"...")},gy=function(e){var t=e.children;return l.createElement(gg,null,t)},gw=function(e){var t=e.loading,n=e.toml,r=e.error,i=void 0===r?"":r,a=e.title,o=e.expanded;if(i)return l.createElement(gy,null,i);if(t)return l.createElement(gv,null);a||(a="TOML");var s={display:"block"};return l.createElement(x.default,null,l.createElement(mP.Z,{defaultExpanded:o},l.createElement(mR.Z,{expandIcon:l.createElement(gh.Z,null)},a),l.createElement(mj.Z,{style:s},l.createElement(gd,{language:"toml",style:gs},n))))},g_=function(){var e=rv(gm,{fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return(null==t?void 0:t.configv2.effective)=="N/A"?l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12},l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"TOML Configuration"}),l.createElement(gw,{title:"V2 config dump:",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.user,showHead:!0})))):l.createElement(l.Fragment,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"TOML Configuration"}),l.createElement(gw,{title:"User specified:",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.user,showHead:!0,expanded:!0}),l.createElement(gw,{title:"Effective (with defaults):",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.effective,showHead:!0})))))},gE=n(34823),gS=function(e){return(0,b.createStyles)({cell:{paddingTop:1.5*e.spacing.unit,paddingBottom:1.5*e.spacing.unit}})},gk=(0,b.withStyles)(gS)(function(e){var t=e.classes,n=(0,A.I0)();(0,l.useEffect)(function(){n((0,ty.DQ)())});var r=(0,A.v9)(gE.N,A.wU);return l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"Node"}),l.createElement(r8.Z,null,l.createElement(r9.Z,null,l.createElement(ir.Z,null,l.createElement(r7.default,{className:t.cell},l.createElement(x.default,null,"Version"),l.createElement(x.default,{variant:"subtitle1",color:"textSecondary"},r.version))),l.createElement(ir.Z,null,l.createElement(r7.default,{className:t.cell},l.createElement(x.default,null,"SHA"),l.createElement(x.default,{variant:"subtitle1",color:"textSecondary"},r.commitSHA))))))}),gx=function(){return l.createElement(ig,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,sm:12,md:8},l.createElement(d.Z,{container:!0},l.createElement(g_,null))),l.createElement(d.Z,{item:!0,sm:12,md:4},l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(gk,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(mN,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(mE,null))))))},gT=function(){return l.createElement(gx,null)},gM=function(){return l.createElement(gT,null)},gO=n(44431),gA=1e18,gL=function(e){return new gO.BigNumber(e).dividedBy(gA).toFixed(8)},gC=function(e){var t=e.keys,n=e.chainID,r=e.hideHeaderTitle;return l.createElement(l.Fragment,null,l.createElement(sl.Z,{title:!r&&"Account Balances",subheader:"Chain ID "+n}),l.createElement(aW.Z,null,l.createElement(w.default,{dense:!1,disablePadding:!0},t&&t.map(function(e,r){return l.createElement(l.Fragment,null,l.createElement(_.default,{disableGutters:!0,key:["acc-balance",n.toString(),r.toString()].join("-")},l.createElement(E.Z,{primary:l.createElement(l.Fragment,null,l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12},l.createElement(op,{title:"Address"}),l.createElement(ob,{value:e.address})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(op,{title:"Native Token Balance"}),l.createElement(ob,{value:e.ethBalance||"--"})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(op,{title:"LINK Balance"}),l.createElement(ob,{value:e.linkBalance?gL(e.linkBalance):"--"}))))})),r+1s&&l.createElement(gB.Z,null,l.createElement(ir.Z,null,l.createElement(r7.default,{className:r.footer},l.createElement(aA.Z,{href:"/runs",component:tz},"View More"))))))});function vt(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vn(){var e=vt(["\n ","\n query FetchRecentJobRuns($offset: Int, $limit: Int) {\n jobRuns(offset: $offset, limit: $limit) {\n results {\n ...RecentJobRunsPayload_ResultsFields\n }\n metadata {\n total\n }\n }\n }\n"]);return vn=function(){return e},e}var vr=5,vi=n0(vn(),g9),va=function(){var e=rv(vi,{variables:{offset:0,limit:vr},fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return l.createElement(ve,{data:t,errorMsg:null==r?void 0:r.message,loading:n,maxRunsSize:vr})},vo=function(e){return(0,b.createStyles)({style:{textAlign:"center",padding:2.5*e.spacing.unit,position:"fixed",left:"0",bottom:"0",width:"100%",borderRadius:0},bareAnchor:{color:e.palette.common.black,textDecoration:"none"}})},vs=(0,b.withStyles)(vo)(function(e){var t=e.classes,n=(0,A.v9)(gE.N,A.wU),r=(0,A.I0)();return(0,l.useEffect)(function(){r((0,ty.DQ)())}),l.createElement(ii.default,{className:t.style},l.createElement(x.default,null,"Chainlink Node ",n.version," at commit"," ",l.createElement("a",{target:"_blank",rel:"noopener noreferrer",href:"https://github.com/smartcontractkit/chainlink/commit/".concat(n.commitSHA),className:t.bareAnchor},n.commitSHA)))}),vu=function(e){return(0,b.createStyles)({cell:{borderColor:e.palette.divider,borderTop:"1px solid",borderBottom:"none",paddingTop:2*e.spacing.unit,paddingBottom:2*e.spacing.unit,paddingLeft:2*e.spacing.unit},block:{display:"block"},overflowEllipsis:{textOverflow:"ellipsis",overflow:"hidden"}})},vc=(0,b.withStyles)(vu)(function(e){var t=e.classes,n=e.job;return l.createElement(ir.Z,null,l.createElement(r7.default,{scope:"row",className:t.cell},l.createElement(d.Z,{container:!0,spacing:0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(ih,{href:"/jobs/".concat(n.id),classes:{linkContent:t.block}},l.createElement(x.default,{className:t.overflowEllipsis,variant:"body1",component:"span",color:"primary"},n.name||n.id))),l.createElement(d.Z,{item:!0,xs:12},l.createElement(x.default,{variant:"body1",color:"textSecondary"},"Created ",l.createElement(aO,{tooltip:!0},n.createdAt))))))});function vl(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vf(){var e=vl(["\n fragment RecentJobsPayload_ResultsFields on Job {\n id\n name\n createdAt\n }\n"]);return vf=function(){return e},e}var vd=n0(vf()),vh=function(){return(0,b.createStyles)({cardHeader:{borderBottom:0},table:{tableLayout:"fixed"}})},vp=(0,b.withStyles)(vh)(function(e){var t,n,r=e.classes,i=e.data,a=e.errorMsg,o=e.loading;return l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"Recent Jobs",className:r.cardHeader}),l.createElement(r8.Z,{className:r.table},l.createElement(r9.Z,null,l.createElement(g$,{visible:o}),l.createElement(gz,{visible:(null===(t=null==i?void 0:i.jobs.results)||void 0===t?void 0:t.length)===0},"No recently created jobs"),l.createElement(gU,{msg:a}),null===(n=null==i?void 0:i.jobs.results)||void 0===n?void 0:n.map(function(e,t){return l.createElement(vc,{job:e,key:t})}))))});function vb(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vm(){var e=vb(["\n ","\n query FetchRecentJobs($offset: Int, $limit: Int) {\n jobs(offset: $offset, limit: $limit) {\n results {\n ...RecentJobsPayload_ResultsFields\n }\n }\n }\n"]);return vm=function(){return e},e}var vg=5,vv=n0(vm(),vd),vy=function(){var e=rv(vv,{variables:{offset:0,limit:vg},fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return l.createElement(vp,{data:t,errorMsg:null==r?void 0:r.message,loading:n})},vw=function(){return l.createElement(ig,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:8},l.createElement(va,null)),l.createElement(d.Z,{item:!0,xs:4},l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(gY,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(vy,null))))),l.createElement(vs,null))},v_=function(){return l.createElement(vw,null)},vE=function(){return l.createElement(v_,null)},vS=n(87239),vk=function(e){switch(e){case"DirectRequestSpec":return"Direct Request";case"FluxMonitorSpec":return"Flux Monitor";default:return e.replace(/Spec$/,"")}},vx=n(5022),vT=n(78718),vM=n.n(vT);function vO(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n1?t-1:0),r=1;r1?t-1:0),r=1;re.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&n.map(function(e){return l.createElement(ir.Z,{key:e.id,style:{cursor:"pointer"},onClick:function(){return r.push("/runs/".concat(e.id))}},l.createElement(r7.default,{className:t.idCell,scope:"row"},l.createElement("div",{className:t.runDetails},l.createElement(x.default,{variant:"h5",color:"primary",component:"span"},e.id))),l.createElement(r7.default,{className:t.stampCell},l.createElement(x.default,{variant:"body1",color:"textSecondary",className:t.stamp},"Created ",l.createElement(aO,{tooltip:!0},e.createdAt))),l.createElement(r7.default,{className:t.statusCell,scope:"row"},l.createElement(x.default,{variant:"body1",className:O()(t.status,yh(t,e.status))},e.status.toLowerCase())))})))}),yb=n(16839),ym=n.n(yb);function yg(e){var t=e.replace(/\w+\s*=\s*<([^>]|[\r\n])*>/g,""),n=ym().read(t),r=n.edges();return n.nodes().map(function(e){var t={id:e,parentIds:r.filter(function(t){return t.w===e}).map(function(e){return e.v})};return Object.keys(n.node(e)).length>0&&(t.attributes=n.node(e)),t})}var yv=n(94164),yy=function(e){var t=e.data,n=[];return(null==t?void 0:t.attributes)&&Object.keys(t.attributes).forEach(function(e){var r;n.push(l.createElement("div",{key:e},l.createElement(x.default,{variant:"body1",color:"textSecondary",component:"div"},l.createElement("b",null,e,":")," ",null===(r=t.attributes)||void 0===r?void 0:r[e])))}),l.createElement("div",null,t&&l.createElement(x.default,{variant:"body1",color:"textPrimary"},l.createElement("b",null,t.id)),n)},yw=n(73343),y_=n(3379),yE=n.n(y_);function yS(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);nwindow.innerWidth?u-r.getBoundingClientRect().width-a:u+a,n=c+r.getBoundingClientRect().height+i>window.innerHeight?c-r.getBoundingClientRect().height-a:c+a,r.style.opacity=String(1),r.style.top="".concat(n,"px"),r.style.left="".concat(t,"px"),r.style.zIndex=String(1)}},h=function(e){var t=document.getElementById("tooltip-d3-chart-".concat(e));t&&(t.style.opacity=String(0),t.style.zIndex=String(-1))};return l.createElement("div",{style:{fontFamily:"sans-serif",fontWeight:"normal"}},l.createElement(yv.kJ,{id:"task-list-graph-d3",data:i,config:s,onMouseOverNode:d,onMouseOutNode:h},"D3 chart"),n.map(function(e){return l.createElement("div",{key:"d3-tooltip-key-".concat(e.id),id:"tooltip-d3-chart-".concat(e.id),style:{position:"absolute",opacity:"0",border:"1px solid rgba(0, 0, 0, 0.1)",padding:yw.r.spacing.unit,background:"white",borderRadius:5,zIndex:-1,inlineSize:"min-content"}},l.createElement(yy,{data:e}))}))};function yL(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);nyY&&l.createElement("div",{className:t.runDetails},l.createElement(aA.Z,{href:"/jobs/".concat(n.id,"/runs"),component:tz},"View more")))),l.createElement(d.Z,{item:!0,xs:12,sm:6},l.createElement(yF,{observationSource:n.observationSource})))});function yH(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&void 0!==arguments[0]?arguments[0]:"";try{return vx.parse(e),!0}catch(t){return!1}})}),wW=function(e){var t=e.initialValues,n=e.onSubmit,r=e.onTOMLChange;return l.createElement(hT,{initialValues:t,validationSchema:wG,onSubmit:n},function(e){var t=e.isSubmitting,n=e.values;return r&&r(n.toml),l.createElement(hR,{"data-testid":"job-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:hX,id:"toml",name:"toml",label:"Job Spec (TOML)",required:!0,fullWidth:!0,multiline:!0,rows:10,rowsMax:25,variant:"outlined",autoComplete:"off",FormHelperTextProps:{"data-testid":"toml-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(ok.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},"Create Job"))))})},wK=n(50109),wV="persistSpec";function wq(e){var t=e.query,n=new URLSearchParams(t).get("definition");return n?(wK.t8(wV,n),{toml:n}):{toml:wK.U2(wV)||""}}var wZ=function(e){var t=e.onSubmit,n=e.onTOMLChange,r=wq({query:(0,h.TH)().search}),i=function(e){var t=e.replace(/[\u200B-\u200D\uFEFF]/g,"");wK.t8("".concat(wV),t),n&&n(t)};return l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"New Job"}),l.createElement(aW.Z,null,l.createElement(wW,{initialValues:r,onSubmit:t,onTOMLChange:i})))};function wX(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n1&&void 0!==arguments[1]?arguments[1]:{},n=t.start,r=void 0===n?6:n,i=t.end,a=void 0===i?4:i;return e.substring(0,r)+"..."+e.substring(e.length-a)}function _M(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(_W,e)},_V=function(){var e=_K({fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error,i=e.refetch;return l.createElement(_U,{loading:n,data:t,errorMsg:null==r?void 0:r.message,refetch:i})},_q=function(e){var t=e.csaKey;return l.createElement(ir.Z,{hover:!0},l.createElement(r7.default,null,l.createElement(x.default,{variant:"body1"},t.publicKey," ",l.createElement(_x,{data:t.publicKey}))))};function _Z(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function _X(){var e=_Z(["\n fragment CSAKeysPayload_ResultsFields on CSAKey {\n id\n publicKey\n }\n"]);return _X=function(){return e},e}var _J=n0(_X()),_Q=function(e){var t,n,r,i=e.data,a=e.errorMsg,o=e.loading,s=e.onCreate;return l.createElement(r5.Z,null,l.createElement(sl.Z,{action:(null===(t=null==i?void 0:i.csaKeys.results)||void 0===t?void 0:t.length)===0&&l.createElement(ok.default,{variant:"outlined",color:"primary",onClick:s},"New CSA Key"),title:"CSA Key",subheader:"Manage your CSA Key"}),l.createElement(r8.Z,null,l.createElement(ie.Z,null,l.createElement(ir.Z,null,l.createElement(r7.default,null,"Public Key"))),l.createElement(r9.Z,null,l.createElement(g$,{visible:o}),l.createElement(gz,{visible:(null===(n=null==i?void 0:i.csaKeys.results)||void 0===n?void 0:n.length)===0}),l.createElement(gU,{msg:a}),null===(r=null==i?void 0:i.csaKeys.results)||void 0===r?void 0:r.map(function(e,t){return l.createElement(_q,{csaKey:e,key:t})}))))};function _1(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(EM,e)};function EA(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(EJ,e)},E3=function(){return oo(EQ)},E4=function(){return oo(E1)},E6=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return rv(E0,e)};function E5(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(SK,e)};function Sq(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function kV(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}var kq=function(e){var t=e.run,n=l.useMemo(function(){var e=t.inputs,n=t.outputs,r=t.taskRuns,i=kK(t,["inputs","outputs","taskRuns"]),a={};try{a=JSON.parse(e)}catch(o){a={}}return kW(kz({},i),{inputs:a,outputs:n,taskRuns:r})},[t]);return l.createElement(r5.Z,null,l.createElement(aW.Z,null,l.createElement(kH,{object:n})))};function kZ(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function kX(e){for(var t=1;t0&&l.createElement(kr,{errors:t.allErrors})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(h.rs,null,l.createElement(h.AW,{path:"".concat(n,"/json")},l.createElement(kq,{run:t})),l.createElement(h.AW,{path:n},t.taskRuns.length>0&&l.createElement(kN,{taskRuns:t.taskRuns,observationSource:t.job.observationSource}))))))))};function k5(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function k8(){var e=k5(["\n ","\n query FetchJobRun($id: ID!) {\n jobRun(id: $id) {\n __typename\n ... on JobRun {\n ...JobRunPayload_Fields\n }\n ... on NotFoundError {\n message\n }\n }\n }\n"]);return k8=function(){return e},e}var k9=n0(k8(),k4),k7=function(){var e=rv(k9,{variables:{id:(0,h.UO)().id}}),t=e.data,n=e.loading,r=e.error;if(n)return l.createElement(iR,null);if(r)return l.createElement(iD,{error:r});var i=null==t?void 0:t.jobRun;switch(null==i?void 0:i.__typename){case"JobRun":return l.createElement(k6,{run:i});case"NotFoundError":return l.createElement(oa,null);default:return null}};function xe(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xt(){var e=xe(["\n fragment JobRunsPayload_ResultsFields on JobRun {\n id\n allErrors\n createdAt\n finishedAt\n status\n job {\n id\n }\n }\n"]);return xt=function(){return e},e}var xn=n0(xt()),xr=function(e){var t=e.loading,n=e.data,r=e.page,i=e.pageSize,a=(0,h.k6)(),o=l.useMemo(function(){return null==n?void 0:n.jobRuns.results.map(function(e){var t,n=e.allErrors,r=e.id,i=e.createdAt;return{id:r,createdAt:i,errors:n,finishedAt:e.finishedAt,status:e.status}})},[n]);return l.createElement(ig,null,l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:12},l.createElement(iy,null,"Job Runs")),t&&l.createElement(iR,null),n&&o&&l.createElement(d.Z,{item:!0,xs:12},l.createElement(r5.Z,null,l.createElement(yp,{runs:o}),l.createElement(it.Z,{component:"div",count:n.jobRuns.metadata.total,rowsPerPage:i,rowsPerPageOptions:[i],page:r-1,onChangePage:function(e,t){a.push("/runs?page=".concat(t+1,"&per=").concat(i))},onChangeRowsPerPage:function(){},backIconButtonProps:{"aria-label":"prev-page"},nextIconButtonProps:{"aria-label":"next-page"}})))))};function xi(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xa(){var e=xi(["\n ","\n query FetchJobRuns($offset: Int, $limit: Int) {\n jobRuns(offset: $offset, limit: $limit) {\n results {\n ...JobRunsPayload_ResultsFields\n }\n metadata {\n total\n }\n }\n }\n"]);return xa=function(){return e},e}var xo=n0(xa(),xn),xs=function(){var e=ij(),t=parseInt(e.get("page")||"1",10),n=parseInt(e.get("per")||"25",10),r=rv(xo,{variables:{offset:(t-1)*n,limit:n},fetchPolicy:"cache-and-network"}),i=r.data,a=r.loading,o=r.error;return o?l.createElement(iD,{error:o}):l.createElement(xr,{loading:a,data:i,page:t,pageSize:n})},xu=function(){var e=(0,h.$B)().path;return l.createElement(h.rs,null,l.createElement(h.AW,{exact:!0,path:e},l.createElement(xs,null)),l.createElement(h.AW,{path:"".concat(e,"/:id")},l.createElement(k7,null)))};function xc(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xl(){var e=xc(["\n fragment FetchFeedsManagersPayload_ResultsFields on FeedsManager {\n __typename\n id\n name\n uri\n publicKey\n isConnectionActive\n createdAt\n }\n query FetchFeedsManagers {\n feedsManagers {\n results {\n ...FetchFeedsManagersPayload_ResultsFields\n }\n }\n }\n"]);return xl=function(){return e},e}var xf=n0(xl()),xd=function(e){return rv(xf,e)},xh=n(47559),xp=n(83165),xb=n(47298),xm=n(81395),xg=function(){return(0,b.createStyles)({root:{display:"flex"},connectedIcon:{color:xh.default[500]},disconnectedIcon:{color:xp.default[500]},text:{marginLeft:4}})},xv=(0,b.withStyles)(xg)(function(e){var t=e.isConnected,n=e.classes;return l.createElement("div",{className:n.root},t?l.createElement(xm.Z,{fontSize:"small",className:n.connectedIcon}):l.createElement(xb.Z,{fontSize:"small",className:n.disconnectedIcon}),l.createElement(x.default,{variant:"body1",inline:!0,className:n.text},t?"Connected":"Disconnected"))}),xy=(0,b.withStyles)(iu)(function(e){var t=e.jobDistributor,n=e.classes;return l.createElement(ir.Z,{className:n.row,hover:!0},l.createElement(r7.default,{className:n.cell,component:"th",scope:"row"},l.createElement(ih,{className:n.link,href:"/job_distributors/".concat(t.id)},t.name)),l.createElement(r7.default,null,l.createElement(xv,{isConnected:t.isConnectionActive})),l.createElement(r7.default,null,_T(t.publicKey,{start:6,end:6}),l.createElement(_x,{data:t.publicKey})),l.createElement(r7.default,null,t.uri))}),xw=function(e){var t=e.jobDistributors;return l.createElement(ig,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:9},l.createElement(iy,null,"Job Distributors")),l.createElement(d.Z,{item:!0,xs:3},l.createElement(d.Z,{container:!0,justify:"flex-end"},l.createElement(d.Z,{item:!0},l.createElement(aA.Z,{variant:"secondary",component:tz,href:"/job_distributors/new"},"New Job Distributor")))),l.createElement(d.Z,{item:!0,xs:12},l.createElement(r5.Z,null,l.createElement(r8.Z,null,l.createElement(ie.Z,null,l.createElement(ir.Z,null,l.createElement(r7.default,null,"Name"),l.createElement(r7.default,null,"Status"),l.createElement(r7.default,null,"CSA Public Key"),l.createElement(r7.default,null,"RPC URL"))),l.createElement(r9.Z,null,0===t.length&&l.createElement(ir.Z,null,l.createElement(r7.default,{component:"th",scope:"row",colSpan:3},"Job Distributors have not been registered")),t.map(function(e){return l.createElement(xy,{key:e.id,jobDistributor:e})})))))))},x_=function(){var e,t=xd({fetchPolicy:"cache-and-network"}),n=t.data,r=t.loading,i=t.error;return r?l.createElement(iR,null):i?l.createElement(iD,{error:i}):l.createElement(xw,{jobDistributors:null!==(e=null==n?void 0:n.feedsManagers.results)&&void 0!==e?e:[]})},xE=bv().shape({name:p0().required("Required"),uri:p0().required("Required"),publicKey:p0().required("Required")}),xS=function(e){var t=e.initialValues,n=e.onSubmit;return l.createElement(hT,{initialValues:t,validationSchema:xE,onSubmit:n},function(e){var t=e.isSubmitting,n=e.submitForm;return l.createElement(hR,{"data-testid":"feeds-manager-form"},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"name",name:"name",label:"Name",required:!0,fullWidth:!0,FormHelperTextProps:{"data-testid":"name-helper-text"}})),l.createElement(d.Z,{item:!0,xs:!1,md:6}),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"uri",name:"uri",label:"URI",required:!0,fullWidth:!0,helperText:"Provided by the Job Distributor operator",FormHelperTextProps:{"data-testid":"uri-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"publicKey",name:"publicKey",label:"Public Key",required:!0,fullWidth:!0,helperText:"Provided by the Job Distributor operator",FormHelperTextProps:{"data-testid":"publicKey-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(ok.default,{variant:"contained",color:"primary",disabled:t,onClick:n},"Submit"))))})},xk=function(e){var t=e.data,n=e.onSubmit,r={name:t.name,uri:t.uri,publicKey:t.publicKey};return l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r5.Z,null,l.createElement(sl.Z,{title:"Edit Job Distributor"}),l.createElement(aW.Z,null,l.createElement(xS,{initialValues:r,onSubmit:n})))))};function xx(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return rv(xZ,e)},xJ=n(57234),xQ={EVM:"EVM",APTOS:"APTOS",SOLANA:"SOLANA",STARKNET:"STARKNET",COSMOS:"COSMOS"};function x1(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function x5(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}function x8(e,t){return x0(e)||x3(e,t)||x9(e,t)||x4()}function x9(e,t){if(e){if("string"==typeof e)return x1(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);if("Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n)return Array.from(n);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return x1(e,t)}}var x7=function(e){return"SN_MAIN"===e||"SN_SEPOLIA"===e},Te=bv().shape({chainID:p0().required("Required"),chainType:p0().required("Required"),accountAddr:p0().required("Required"),accountAddrPubKey:p0().nullable(),adminAddr:p0().required("Required"),ocr1Multiaddr:p0().when(["ocr1Enabled","ocr1IsBootstrap"],{is:function(e,t){return e&&t},then:p0().required("Required").nullable()}).nullable(),ocr1P2PPeerID:p0().when(["ocr1Enabled","ocr1IsBootstrap"],{is:function(e,t){return e&&!t},then:p0().required("Required").nullable()}).nullable(),ocr1KeyBundleID:p0().when(["ocr1Enabled","ocr1IsBootstrap"],{is:function(e,t){return e&&!t},then:p0().required("Required").nullable()}).nullable(),ocr2Multiaddr:p0().when(["ocr2Enabled","ocr2IsBootstrap"],{is:function(e,t){return e&&t},then:p0().required("Required").nullable()}).nullable(),ocr2P2PPeerID:p0().when(["ocr2Enabled","ocr2IsBootstrap"],{is:function(e,t){return e&&!t},then:p0().required("Required").nullable()}).nullable(),ocr2KeyBundleID:p0().when(["ocr2Enabled","ocr2IsBootstrap"],{is:function(e,t){return e&&!t},then:p0().required("Required").nullable()}).nullable(),ocr2CommitPluginEnabled:pV().required("Required"),ocr2ExecutePluginEnabled:pV().required("Required"),ocr2MedianPluginEnabled:pV().required("Required"),ocr2MercuryPluginEnabled:pV().required("Required"),ocr2ForwarderAddress:p0().nullable()}),Tt=function(e){return(0,b.createStyles)({supportedJobOptionsPaper:{padding:2*e.spacing.unit}})},Tn=function(e){var t=e.addresses,n=x6(e,["addresses"]),r=h_(),i=r.values,a=i.chainID,o=i.accountAddr,s=r.setFieldValue,u=x8(l.useState(!1),2),c=u[0],f=u[1],d=l.useRef();l.useEffect(function(){d.current=a},[a]),l.useEffect(function(){a!==d.current&&(s(n.name,""),f(!1))},[a,s,n.name]);var h=function(e){var t=e.target.value;"custom"===t?(f(!0),s(n.name,"")):(f(!1),s(n.name,t))};return l.createElement(l.Fragment,null,!x7(a)&&l.createElement(hP,x2({},n,{select:!0,value:c?"custom":o,onChange:h}),t.map(function(e){return l.createElement(tE.default,{key:e,value:e},e)})),x7(a)&&l.createElement(hP,{component:hX,id:"accountAddr",name:"accountAddr",label:"Enter your account address",inputProps:{"data-testid":"customAccountAddr-input"},helperText:"The account address used for this chain",required:!0,fullWidth:!0}),x7(a)&&l.createElement("div",null,l.createElement(hP,{component:hX,id:"accountAddrPubKey",name:"accountAddrPubKey",label:"Account Address Public Key",required:!0,fullWidth:!0,helperText:"The public key for your account address",FormHelperTextProps:{"data-testid":"accountAddrPubKey-helper-text"}})))},Tr=(0,b.withStyles)(Tt)(function(e){var t=e.classes,n=e.editing,r=void 0!==n&&n,i=e.innerRef,a=e.initialValues,o=e.onSubmit,s=e.chains,u=void 0===s?[]:s,c=e.accountsEVM,f=void 0===c?[]:c,h=e.accountsAptos,p=void 0===h?[]:h,b=e.p2pKeys,m=void 0===b?[]:b,g=e.ocrKeys,v=void 0===g?[]:g,y=e.ocr2Keys,w=void 0===y?[]:y,_=e.showSubmit,E=void 0!==_&&_;return l.createElement(hT,{innerRef:i,initialValues:a,validationSchema:Te,onSubmit:o},function(e){var n=e.values,i=[];return n.chainType===xQ.EVM&&(i=f.filter(function(e){return e.chain.id==n.chainID&&!e.isDisabled}).map(function(e){return e.address})),n.chainType===xQ.APTOS&&(i=p.map(function(e){return e.account})),l.createElement(hR,{"data-testid":"feeds-manager-form",id:"chain-configuration-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"chainType",name:"chainType",label:"Chain Type",select:!0,required:!0,fullWidth:!0,disabled:r},l.createElement(tE.default,{key:xQ.EVM,value:xQ.EVM},"EVM"),l.createElement(tE.default,{key:xQ.APTOS,value:xQ.APTOS},"APTOS"))),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"chainID",name:"chainID",label:"Chain ID",required:!0,fullWidth:!0,select:!0,disabled:r,inputProps:{"data-testid":"chainID-input"},FormHelperTextProps:{"data-testid":"chainID-helper-text"}},u.filter(function(e){return e.network.toUpperCase()===n.chainType}).map(function(e){return l.createElement(tE.default,{key:e.id,value:e.id},e.id)}))),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(Tn,{component:hX,id:"accountAddr",name:"accountAddr",label:"Account Address",inputProps:{"data-testid":"accountAddr-input"},required:!0,fullWidth:!0,select:!0,helperText:"The account address used for this chain",addresses:i,FormHelperTextProps:{"data-testid":"accountAddr-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"adminAddr",name:"adminAddr",label:"Admin Address",required:!0,fullWidth:!0,helperText:"The address used for LINK payments",FormHelperTextProps:{"data-testid":"adminAddr-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(x.default,null,"Supported Job Types")),l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"fluxMonitorEnabled",type:"checkbox",Label:{label:"Flux Monitor"}})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"ocr1Enabled",type:"checkbox",Label:{label:"OCR"}}),n.ocr1Enabled&&l.createElement(ii.default,{className:t.supportedJobOptionsPaper},l.createElement(d.Z,{container:!0,spacing:8},l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"ocr1IsBootstrap",type:"checkbox",Label:{label:"Is this node running as a bootstrap peer?"}})),n.ocr1IsBootstrap?l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:hX,id:"ocr1Multiaddr",name:"ocr1Multiaddr",label:"Multiaddr",required:!0,fullWidth:!0,helperText:"The OCR Multiaddr which oracles use to query for network information",FormHelperTextProps:{"data-testid":"ocr1Multiaddr-helper-text"}})):l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"ocr1P2PPeerID",name:"ocr1P2PPeerID",label:"Peer ID",select:!0,required:!0,fullWidth:!0,helperText:"The Peer ID used for this chain",FormHelperTextProps:{"data-testid":"ocr1P2PPeerID-helper-text"}},m.map(function(e){return l.createElement(tE.default,{key:e.peerID,value:e.peerID},e.peerID)}))),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"ocr1KeyBundleID",name:"ocr1KeyBundleID",label:"Key Bundle ID",select:!0,required:!0,fullWidth:!0,helperText:"The OCR Key Bundle ID used for this chain",FormHelperTextProps:{"data-testid":"ocr1KeyBundleID-helper-text"}},v.map(function(e){return l.createElement(tE.default,{key:e.id,value:e.id},e.id)})))))))),l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"ocr2Enabled",type:"checkbox",Label:{label:"OCR2"}}),n.ocr2Enabled&&l.createElement(ii.default,{className:t.supportedJobOptionsPaper},l.createElement(d.Z,{container:!0,spacing:8},l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:h2,name:"ocr2IsBootstrap",type:"checkbox",Label:{label:"Is this node running as a bootstrap peer?"}})),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"ocr2P2PPeerID",name:"ocr2P2PPeerID",label:"Peer ID",select:!0,required:!n.ocr2IsBootstrap,fullWidth:!0,helperText:"The Peer ID used for this chain",FormHelperTextProps:{"data-testid":"ocr2P2PPeerID-helper-text"}},m.map(function(e){return l.createElement(tE.default,{key:e.peerID,value:e.peerID},e.peerID)}))),n.ocr2IsBootstrap?l.createElement(d.Z,{item:!0,xs:12},l.createElement(hP,{component:hX,id:"ocr2Multiaddr",name:"ocr2Multiaddr",label:"Multiaddr",required:!0,fullWidth:!0,helperText:"The OCR2 Multiaddr which oracles use to query for network information",FormHelperTextProps:{"data-testid":"ocr2Multiaddr-helper-text"}})):l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hP,{component:hX,id:"ocr2KeyBundleID",name:"ocr2KeyBundleID",label:"Key Bundle ID",select:!0,required:!0,fullWidth:!0,helperText:"The OCR2 Key Bundle ID used for this chain",FormHelperTextProps:{"data-testid":"ocr2KeyBundleID-helper-text"}},w.map(function(e){return l.createElement(tE.default,{key:e.id,value:e.id},e.id)}))),l.createElement(d.Z,{item:!0,xs:12},l.createElement(x.default,null,"Supported Plugins")),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2CommitPluginEnabled",type:"checkbox",Label:{label:"Commit"}})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2ExecutePluginEnabled",type:"checkbox",Label:{label:"Execute"}})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2RebalancerPluginEnabled",type:"checkbox",Label:{label:"Rebalancer"}})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2MedianPluginEnabled",type:"checkbox",Label:{label:"Median"}})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(hP,{component:h2,name:"ocr2MercuryPluginEnabled",type:"checkbox",Label:{label:"Mercury"}})),l.createElement(d.Z,{item:!0,xs:12,md:12},l.createElement(hP,{component:hX,id:"ocr2ForwarderAddress",name:"ocr2ForwarderAddress",label:"Forwarder Address (optional)",fullWidth:!0,helperText:"The forwarder address from the Operator Forwarder Contract",FormHelperTextProps:{"data-testid":"ocr2ForwarderAddress-helper-text"}}))))))),E&&l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(ok.default,{variant:"contained",color:"primary",type:"submit",size:"large"},"Submit"))))})});function Ti(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function Ta(){var e=Ti(["\n fragment AptosKeysPayload_ResultsFields on AptosKey {\n account\n id\n }\n"]);return Ta=function(){return e},e}function To(){var e=Ti(["\n ","\n query FetchAptosKeys {\n aptosKeys {\n results {\n ...AptosKeysPayload_ResultsFields\n }\n }\n }\n"]);return To=function(){return e},e}var Ts=n0(Ta()),Tu=n0(To(),Ts),Tc=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return rv(Tu,e)},Tl=function(e){var t=e.onClose,n=e.open,r=e.onSubmit,i=l.useRef(),a=i$({fetchPolicy:"network-only"}).data,o=_K({fetchPolicy:"cache-and-network"}).data,s=Tc({fetchPolicy:"cache-and-network"}).data,u=SV({fetchPolicy:"cache-and-network"}).data,c=EO({fetchPolicy:"cache-and-network"}).data,f=E2({fetchPolicy:"cache-and-network"}).data,d={chainID:"",chainType:xQ.EVM,accountAddr:"",adminAddr:"",accountAddrPubKey:"",fluxMonitorEnabled:!1,ocr1Enabled:!1,ocr1IsBootstrap:!1,ocr1Multiaddr:"",ocr1P2PPeerID:"",ocr1KeyBundleID:"",ocr2Enabled:!1,ocr2IsBootstrap:!1,ocr2Multiaddr:"",ocr2P2PPeerID:"",ocr2KeyBundleID:"",ocr2CommitPluginEnabled:!1,ocr2ExecutePluginEnabled:!1,ocr2MedianPluginEnabled:!1,ocr2MercuryPluginEnabled:!1,ocr2RebalancerPluginEnabled:!1,ocr2ForwarderAddress:""},h=a?a.chains.results:[],p=o?o.ethKeys.results:[],b=s?s.aptosKeys.results:[],m=u?u.p2pKeys.results:[],g=c?c.ocrKeyBundles.results:[],v=f?f.ocr2KeyBundles.results:[];return l.createElement(aD.Z,{onClose:t,open:n,disableBackdropClick:!0},l.createElement(oO.Z,{disableTypography:!0},l.createElement(x.default,{variant:"body2"},"New Supported Chain")),l.createElement(oT.Z,null,l.createElement(Tr,{innerRef:i,initialValues:d,onSubmit:r,chains:h,accountsEVM:p,accountsAptos:b,p2pKeys:m,ocrKeys:g,ocr2Keys:v})),l.createElement(ox.Z,null,l.createElement(ok.default,{onClick:t},"Cancel"),l.createElement(ok.default,{color:"primary",type:"submit",form:"chain-configuration-form",variant:"contained"},"Submit")))},Tf=function(e){var t=e.cfg,n=e.onClose,r=e.open,i=e.onSubmit,a=l.useRef(),o=i$({fetchPolicy:"network-only"}).data,s=_K({fetchPolicy:"cache-and-network"}).data,u=Tc({fetchPolicy:"cache-and-network"}).data,c=SV({fetchPolicy:"cache-and-network"}).data,f=EO({fetchPolicy:"cache-and-network"}).data,d=E2({fetchPolicy:"cache-and-network"}).data;if(!t)return null;var h={chainID:t.chainID,chainType:xQ.EVM,accountAddr:t.accountAddr,adminAddr:t.adminAddr,accountAddrPubKey:t.accountAddrPubKey,fluxMonitorEnabled:t.fluxMonitorJobConfig.enabled,ocr1Enabled:t.ocr1JobConfig.enabled,ocr1IsBootstrap:t.ocr1JobConfig.isBootstrap,ocr1Multiaddr:t.ocr1JobConfig.multiaddr,ocr1P2PPeerID:t.ocr1JobConfig.p2pPeerID,ocr1KeyBundleID:t.ocr1JobConfig.keyBundleID,ocr2Enabled:t.ocr2JobConfig.enabled,ocr2IsBootstrap:t.ocr2JobConfig.isBootstrap,ocr2Multiaddr:t.ocr2JobConfig.multiaddr,ocr2P2PPeerID:t.ocr2JobConfig.p2pPeerID,ocr2KeyBundleID:t.ocr2JobConfig.keyBundleID,ocr2CommitPluginEnabled:t.ocr2JobConfig.plugins.commit,ocr2ExecutePluginEnabled:t.ocr2JobConfig.plugins.execute,ocr2MedianPluginEnabled:t.ocr2JobConfig.plugins.median,ocr2MercuryPluginEnabled:t.ocr2JobConfig.plugins.mercury,ocr2RebalancerPluginEnabled:t.ocr2JobConfig.plugins.rebalancer,ocr2ForwarderAddress:t.ocr2JobConfig.forwarderAddress},p=o?o.chains.results:[],b=s?s.ethKeys.results:[],m=u?u.aptosKeys.results:[],g=c?c.p2pKeys.results:[],v=f?f.ocrKeyBundles.results:[],y=d?d.ocr2KeyBundles.results:[];return l.createElement(aD.Z,{onClose:n,open:r,disableBackdropClick:!0},l.createElement(oO.Z,{disableTypography:!0},l.createElement(x.default,{variant:"body2"},"Edit Supported Chain")),l.createElement(oT.Z,null,l.createElement(Tr,{innerRef:a,initialValues:h,onSubmit:i,chains:p,accountsEVM:b,accountsAptos:m,p2pKeys:g,ocrKeys:v,ocr2Keys:y,editing:!0})),l.createElement(ox.Z,null,l.createElement(ok.default,{onClick:n},"Cancel"),l.createElement(ok.default,{color:"primary",type:"submit",form:"chain-configuration-form",variant:"contained"},"Submit")))};function Td(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);nt.version?e:t})},[o]),g=l.useMemo(function(){return MI(o).sort(function(e,t){return t.version-e.version})},[o]),v=function(e,t,n){switch(e){case"PENDING":return l.createElement(l.Fragment,null,l.createElement(ok.default,{variant:"text",color:"secondary",onClick:function(){return b("reject",t)}},"Reject"),m.id===t&&"DELETED"!==n.status&&"REVOKED"!==n.status&&l.createElement(ok.default,{variant:"contained",color:"primary",onClick:function(){return b("approve",t)}},"Approve"),m.id===t&&"DELETED"===n.status&&n.pendingUpdate&&l.createElement(l.Fragment,null,l.createElement(ok.default,{variant:"contained",color:"primary",onClick:function(){return b("cancel",t)}},"Cancel"),l.createElement(x.default,{color:"error"},"This proposal was deleted. Cancel the spec to delete any running jobs")));case"APPROVED":return l.createElement(l.Fragment,null,l.createElement(ok.default,{variant:"contained",onClick:function(){return b("cancel",t)}},"Cancel"),"DELETED"===n.status&&n.pendingUpdate&&l.createElement(x.default,{color:"error"},"This proposal was deleted. Cancel the spec to delete any running jobs"));case"CANCELLED":if(m.id===t&&"DELETED"!==n.status&&"REVOKED"!==n.status)return l.createElement(ok.default,{variant:"contained",color:"primary",onClick:function(){return b("approve",t)}},"Approve");return null;default:return null}};return l.createElement("div",null,g.map(function(e,n){return l.createElement(mP.Z,{defaultExpanded:0===n,key:n},l.createElement(mR.Z,{expandIcon:l.createElement(gh.Z,null)},l.createElement(x.default,{className:t.versionText},"Version ",e.version),l.createElement(Es.Z,{label:e.status,color:"APPROVED"===e.status?"primary":"default",variant:"REJECTED"===e.status||"CANCELLED"===e.status?"outlined":"default"}),l.createElement("div",{className:t.proposedAtContainer},l.createElement(x.default,null,"Proposed ",l.createElement(aO,{tooltip:!0},e.createdAt)))),l.createElement(mj.Z,{className:t.expansionPanelDetails},l.createElement("div",{className:t.actions},l.createElement("div",{className:t.editContainer},0===n&&("PENDING"===e.status||"CANCELLED"===e.status)&&"DELETED"!==s.status&&"REVOKED"!==s.status&&l.createElement(ok.default,{variant:"contained",onClick:function(){return p(!0)}},"Edit")),l.createElement("div",{className:t.actionsContainer},v(e.status,e.id,s))),l.createElement(gd,{language:"toml",style:gs,"data-testid":"codeblock"},e.definition)))}),l.createElement(oC,{open:null!=c,title:c?Mj[c.action].title:"",body:c?Mj[c.action].body:"",onConfirm:function(){if(c){switch(c.action){case"approve":n(c.id);break;case"cancel":r(c.id);break;case"reject":i(c.id)}f(null)}},cancelButtonText:"Cancel",onCancel:function(){return f(null)}}),l.createElement(ME,{open:h,onClose:function(){return p(!1)},initialValues:{definition:m.definition,id:m.id},onSubmit:a}))});function MY(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function MB(){var e=MY(["\n ","\n fragment JobProposalPayloadFields on JobProposal {\n id\n externalJobID\n remoteUUID\n jobID\n specs {\n ...JobProposal_SpecsFields\n }\n status\n pendingUpdate\n }\n"]);return MB=function(){return e},e}var MU=n0(MB(),MP),MH=function(e){var t=e.onApprove,n=e.onCancel,r=e.onReject,i=e.onUpdateSpec,a=e.proposal;return l.createElement(ig,null,l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:9},l.createElement(iy,null,"Job Proposal #",a.id))),l.createElement(Mm,{proposal:a}),l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:9},l.createElement(T6,null,"Specs"))),l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:12},l.createElement(MF,{proposal:a,specs:a.specs,onReject:r,onApprove:t,onCancel:n,onUpdateSpec:i}))))};function M$(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);nU,tA:()=>$,KL:()=>H,Iw:()=>V,DQ:()=>W,cB:()=>T,LO:()=>M,t5:()=>k,qt:()=>x,Jc:()=>C,L7:()=>Y,EO:()=>B});var r,i,a=n(66289),o=n(41800),s=n.n(o),u=n(67932);(i=r||(r={})).IN_PROGRESS="in_progress",i.PENDING_INCOMING_CONFIRMATIONS="pending_incoming_confirmations",i.PENDING_CONNECTION="pending_connection",i.PENDING_BRIDGE="pending_bridge",i.PENDING_SLEEP="pending_sleep",i.ERRORED="errored",i.COMPLETED="completed";var c=n(87013),l=n(19084),f=n(34823);function d(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]j,v2:()=>F});var r=n(66289);function i(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var a="/sessions",o="/sessions",s=function e(t){var n=this;i(this,e),this.api=t,this.createSession=function(e){return n.create(e)},this.destroySession=function(){return n.destroy()},this.create=this.api.createResource(a),this.destroy=this.api.deleteResource(o)};function u(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var c="/v2/bulk_delete_runs",l=function e(t){var n=this;u(this,e),this.api=t,this.bulkDeleteJobRuns=function(e){return n.destroy(e)},this.destroy=this.api.deleteResource(c)};function f(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var d="/v2/chains/evm",h="".concat(d,"/:id"),p=function e(t){var n=this;f(this,e),this.api=t,this.getChains=function(){return n.index()},this.createChain=function(e){return n.create(e)},this.destroyChain=function(e){return n.destroy(void 0,{id:e})},this.updateChain=function(e,t){return n.update(t,{id:e})},this.index=this.api.fetchResource(d),this.create=this.api.createResource(d),this.destroy=this.api.deleteResource(h),this.update=this.api.updateResource(h)};function b(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var m="/v2/keys/evm/chain",g=function e(t){var n=this;b(this,e),this.api=t,this.chain=function(e){var t=new URLSearchParams;t.append("address",e.address),t.append("evmChainID",e.evmChainID),null!==e.nextNonce&&t.append("nextNonce",e.nextNonce),null!==e.abandon&&t.append("abandon",String(e.abandon)),null!==e.enabled&&t.append("enabled",String(e.enabled));var r=m+"?"+t.toString();return n.api.createResource(r)()}};function v(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var y="/v2/jobs",w="".concat(y,"/:specId/runs"),_=function e(t){var n=this;v(this,e),this.api=t,this.createJobRunV2=function(e,t){return n.post(t,{specId:e})},this.post=this.api.createResource(w,!0)};function E(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var S="/v2/log",k=function e(t){var n=this;E(this,e),this.api=t,this.getLogConfig=function(){return n.show()},this.updateLogConfig=function(e){return n.update(e)},this.show=this.api.fetchResource(S),this.update=this.api.updateResource(S)};function x(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var T="/v2/nodes",M=function e(t){var n=this;x(this,e),this.api=t,this.getNodes=function(){return n.index()},this.createNode=function(e){return n.create(e)},this.index=this.api.fetchResource(T),this.create=this.api.createResource(T)};function O(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var A="/v2/enroll_webauthn",L=function e(t){var n=this;O(this,e),this.api=t,this.beginKeyRegistration=function(e){return n.create(e)},this.finishKeyRegistration=function(e){return n.put(e)},this.create=this.api.fetchResource(A),this.put=this.api.createResource(A)};function C(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var I="/v2/build_info",D=function e(t){var n=this;C(this,e),this.api=t,this.show=function(){return n.api.GET(I)()}};function N(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var P=function e(t){N(this,e),this.api=t,this.buildInfo=new D(this.api),this.bulkDeleteRuns=new l(this.api),this.chains=new p(this.api),this.logConfig=new k(this.api),this.nodes=new M(this.api),this.jobs=new _(this.api),this.webauthn=new L(this.api),this.evmKeys=new g(this.api)},R=new r.V0({base:void 0}),j=new s(R),F=new P(R)},1398(e,t,n){"use strict";n.d(t,{Z:()=>d});var r=n(67294),i=n(32316),a=n(83638),o=n(94184),s=n.n(o);function u(){return(u=Object.assign||function(e){for(var t=1;tc});var r=n(67294),i=n(32316);function a(){return(a=Object.assign||function(e){for(var t=1;tx,jK:()=>v});var r=n(67294),i=n(37703),a=n(45697),o=n.n(a),s=n(82204),u=n(71426),c=n(94184),l=n.n(c),f=n(32316),d=function(e){var t=e.palette.success||{},n=e.palette.warning||{};return{base:{paddingLeft:5*e.spacing.unit,paddingRight:5*e.spacing.unit},success:{backgroundColor:t.main,color:t.contrastText},error:{backgroundColor:e.palette.error.dark,color:e.palette.error.contrastText},warning:{backgroundColor:n.contrastText,color:n.main}}},h=function(e){var t,n=e.success,r=e.error,i=e.warning,a=e.classes,o=e.className;return n?t=a.success:r?t=a.error:i&&(t=a.warning),l()(a.base,o,t)},p=function(e){return r.createElement(s.Z,{className:h(e),square:!0},r.createElement(u.default,{variant:"body2",color:"inherit",component:"div"},e.children))};p.defaultProps={success:!1,error:!1,warning:!1},p.propTypes={success:o().bool,error:o().bool,warning:o().bool};let b=(0,f.withStyles)(d)(p);var m=function(){return r.createElement(r.Fragment,null,"Unhandled error. Please help us by opening a"," ",r.createElement("a",{href:"https://github.com/smartcontractkit/chainlink/issues/new"},"bug report"))};let g=m;function v(e){return"string"==typeof e?e:e.component?e.component(e.props):r.createElement(g,null)}function y(e,t){var n;return n="string"==typeof e?e:e.component?e.component(e.props):r.createElement(g,null),r.createElement("p",{key:t},n)}var w=function(e){var t=e.notifications;return r.createElement(b,{error:!0},t.map(y))},_=function(e){var t=e.notifications;return r.createElement(b,{success:!0},t.map(y))},E=function(e){var t=e.errors,n=e.successes;return r.createElement("div",null,(null==t?void 0:t.length)>0&&r.createElement(w,{notifications:t}),n.length>0&&r.createElement(_,{notifications:n}))},S=function(e){return{errors:e.notifications.errors,successes:e.notifications.successes}},k=(0,i.$j)(S)(E);let x=k},9409(e,t,n){"use strict";n.d(t,{ZP:()=>j});var r=n(67294),i=n(37703),a=n(5977),o=n(32316),s=n(1398),u=n(82204),c=n(30060),l=n(71426),f=n(60520),d=n(39814),h=n(57209),p=n(26842),b=n(3950),m=n(5536),g=n(45697),v=n.n(g);let y=n.p+"9f6d832ef97e8493764e.svg";function w(){return(w=Object.assign||function(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&_.map(function(e,t){return r.createElement(d.Z,{item:!0,xs:12,key:t},r.createElement(u.Z,{raised:!1,className:v.error},r.createElement(c.Z,null,r.createElement(l.default,{variant:"body1",className:v.errorText},(0,b.jK)(e)))))}),r.createElement(d.Z,{item:!0,xs:12},r.createElement(f.Z,{id:"email",label:"Email",margin:"normal",value:n,onChange:m("email"),error:_.length>0,variant:"outlined",fullWidth:!0})),r.createElement(d.Z,{item:!0,xs:12},r.createElement(f.Z,{id:"password",label:"Password",type:"password",autoComplete:"password",margin:"normal",value:h,onChange:m("password"),error:_.length>0,variant:"outlined",fullWidth:!0})),r.createElement(d.Z,{item:!0,xs:12},r.createElement(d.Z,{container:!0,spacing:0,justify:"center"},r.createElement(d.Z,{item:!0},r.createElement(s.Z,{type:"submit",variant:"primary"},"Access Account")))),y&&r.createElement(l.default,{variant:"body1",color:"textSecondary"},"Signing in...")))))))},P=function(e){return{fetching:e.authentication.fetching,authenticated:e.authentication.allowed,errors:e.notifications.errors}},R=(0,i.$j)(P,x({submitSignIn:p.L7}))(N);let j=(0,h.wU)(e)((0,o.withStyles)(D)(R))},16353(e,t,n){"use strict";n.d(t,{ZP:()=>H,rH:()=>U});var r,i=n(37703),a=n(97779),o=n(9541),s=n(19084);function u(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function c(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:h,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case s.Mk.RECEIVE_SIGNOUT_SUCCESS:case s.Mk.RECEIVE_SIGNIN_SUCCESS:var n={allowed:t.authenticated};return o.Ks(n),f(c({},e,n),{errors:[]});case s.Mk.RECEIVE_SIGNIN_FAIL:var r={allowed:!1};return o.Ks(r),f(c({},e,r),{errors:[]});case s.Mk.RECEIVE_SIGNIN_ERROR:case s.Mk.RECEIVE_SIGNOUT_ERROR:var i={allowed:!1};return o.Ks(i),f(c({},e,i),{errors:t.errors||[]});default:return e}};let b=p;function m(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function g(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:_,t=arguments.length>1?arguments[1]:void 0;return t.type?t.type.startsWith(r.REQUEST)?y(g({},e),{count:e.count+1}):t.type.startsWith(r.RECEIVE)?y(g({},e),{count:Math.max(e.count-1,0)}):t.type.startsWith(r.RESPONSE)?y(g({},e),{count:Math.max(e.count-1,0)}):t.type===s.di.REDIRECT?y(g({},e),{count:0}):e:e};let S=E;function k(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function x(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:O,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case s.di.MATCH_ROUTE:return M(x({},O),{currentUrl:t.pathname});case s.Ih.NOTIFY_SUCCESS:var n={component:t.component,props:t.props};return M(x({},e),{successes:[n],errors:[]});case s.Ih.NOTIFY_SUCCESS_MSG:return M(x({},e),{successes:[t.msg],errors:[]});case s.Ih.NOTIFY_ERROR:var r=t.error.errors,i=null==r?void 0:r.map(function(e){return L(t,e)});return M(x({},e),{successes:[],errors:i});case s.Ih.NOTIFY_ERROR_MSG:return M(x({},e),{successes:[],errors:[t.msg]});case s.Mk.RECEIVE_SIGNIN_FAIL:return M(x({},e),{successes:[],errors:["Your email or password is incorrect. Please try again"]});default:return e}};function L(e,t){return{component:e.component,props:{msg:t.detail}}}let C=A;function I(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function D(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:R,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case s.di.REDIRECT:return P(D({},e),{to:t.to});case s.di.MATCH_ROUTE:return P(D({},e),{to:void 0});default:return e}};let F=j;var Y=n(87013),B=(0,a.UY)({authentication:b,fetching:S,notifications:C,redirect:F,buildInfo:Y.Z});B(void 0,{type:"INITIAL_STATE"});var U=i.v9;let H=B},19084(e,t,n){"use strict";var r,i,a,o,s,u,c,l,f,d;n.d(t,{Ih:()=>i,Mk:()=>a,Y0:()=>s,di:()=>r,jp:()=>o}),n(67294),(u=r||(r={})).REDIRECT="REDIRECT",u.MATCH_ROUTE="MATCH_ROUTE",(c=i||(i={})).NOTIFY_SUCCESS="NOTIFY_SUCCESS",c.NOTIFY_SUCCESS_MSG="NOTIFY_SUCCESS_MSG",c.NOTIFY_ERROR="NOTIFY_ERROR",c.NOTIFY_ERROR_MSG="NOTIFY_ERROR_MSG",(l=a||(a={})).REQUEST_SIGNIN="REQUEST_SIGNIN",l.RECEIVE_SIGNIN_SUCCESS="RECEIVE_SIGNIN_SUCCESS",l.RECEIVE_SIGNIN_FAIL="RECEIVE_SIGNIN_FAIL",l.RECEIVE_SIGNIN_ERROR="RECEIVE_SIGNIN_ERROR",l.RECEIVE_SIGNOUT_SUCCESS="RECEIVE_SIGNOUT_SUCCESS",l.RECEIVE_SIGNOUT_ERROR="RECEIVE_SIGNOUT_ERROR",(f=o||(o={})).RECEIVE_CREATE_ERROR="RECEIVE_CREATE_ERROR",f.RECEIVE_CREATE_SUCCESS="RECEIVE_CREATE_SUCCESS",f.RECEIVE_DELETE_ERROR="RECEIVE_DELETE_ERROR",f.RECEIVE_DELETE_SUCCESS="RECEIVE_DELETE_SUCCESS",f.RECEIVE_UPDATE_ERROR="RECEIVE_UPDATE_ERROR",f.RECEIVE_UPDATE_SUCCESS="RECEIVE_UPDATE_SUCCESS",f.REQUEST_CREATE="REQUEST_CREATE",f.REQUEST_DELETE="REQUEST_DELETE",f.REQUEST_UPDATE="REQUEST_UPDATE",f.UPSERT_CONFIGURATION="UPSERT_CONFIGURATION",f.UPSERT_JOB_RUN="UPSERT_JOB_RUN",f.UPSERT_JOB_RUNS="UPSERT_JOB_RUNS",f.UPSERT_TRANSACTION="UPSERT_TRANSACTION",f.UPSERT_TRANSACTIONS="UPSERT_TRANSACTIONS",f.UPSERT_BUILD_INFO="UPSERT_BUILD_INFO",(d=s||(s={})).FETCH_BUILD_INFO_REQUESTED="FETCH_BUILD_INFO_REQUESTED",d.FETCH_BUILD_INFO_SUCCEEDED="FETCH_BUILD_INFO_SUCCEEDED",d.FETCH_BUILD_INFO_FAILED="FETCH_BUILD_INFO_FAILED"},87013(e,t,n){"use strict";n.d(t,{Y:()=>o,Z:()=>u});var r=n(19084);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:o,t=arguments.length>1?arguments[1]:void 0;return t.type===r.Y0.FETCH_BUILD_INFO_SUCCEEDED?a({},t.buildInfo):e};let u=s},34823(e,t,n){"use strict";n.d(t,{N:()=>r});var r=function(e){return e.buildInfo}},73343(e,t,n){"use strict";n.d(t,{r:()=>u});var r=n(19350),i=n(32316),a=n(59114),o=n(5324),s={props:{MuiGrid:{spacing:3*o.default.unit},MuiCardHeader:{titleTypographyProps:{color:"secondary"}}},palette:{action:{hoverOpacity:.3},primary:{light:"#E5F1FF",main:"#3c40c6",contrastText:"#fff"},secondary:{main:"#3d5170"},success:{light:"#e8faf1",main:r.ek.A700,dark:r.ek[700],contrastText:r.y0.white},warning:{light:"#FFFBF1",main:"#fff6b6",contrastText:"#fad27a"},error:{light:"#ffdada",main:"#f44336",dark:"#d32f2f",contrastText:"#fff"},background:{default:"#f5f6f8",appBar:"#3c40c6"},text:{primary:(0,a.darken)(r.BA.A700,.7),secondary:"#818ea3"},listPendingStatus:{background:"#fef7e5",color:"#fecb4c"},listCompletedStatus:{background:"#e9faf2",color:"#4ed495"}},shape:{borderRadius:o.default.unit},overrides:{MuiButton:{root:{borderRadius:o.default.unit/2,textTransform:"none"},sizeLarge:{padding:void 0,fontSize:void 0,paddingTop:o.default.unit,paddingBottom:o.default.unit,paddingLeft:5*o.default.unit,paddingRight:5*o.default.unit}},MuiTableCell:{body:{fontSize:"1rem"},head:{fontSize:"1rem",fontWeight:400}},MuiCardHeader:{root:{borderBottom:"1px solid rgba(0, 0, 0, 0.12)"},action:{marginTop:-2,marginRight:0,"& >*":{marginLeft:2*o.default.unit}},subheader:{marginTop:.5*o.default.unit}}},typography:{useNextVariants:!0,fontFamily:"-apple-system,BlinkMacSystemFont,Roboto,Helvetica,Arial,sans-serif",button:{textTransform:"none",fontSize:"1.2em"},body1:{fontSize:"1.0rem",fontWeight:400,lineHeight:"1.46429em",color:"rgba(0, 0, 0, 0.87)",letterSpacing:-.4},body2:{fontSize:"1.0rem",fontWeight:500,lineHeight:"1.71429em",color:"rgba(0, 0, 0, 0.87)",letterSpacing:-.4},body1Next:{color:"rgb(29, 29, 29)",fontWeight:400,fontSize:"1rem",lineHeight:1.5,letterSpacing:-.4},body2Next:{color:"rgb(29, 29, 29)",fontWeight:400,fontSize:"0.875rem",lineHeight:1.5,letterSpacing:-.4},display1:{color:"#818ea3",fontSize:"2.125rem",fontWeight:400,lineHeight:"1.20588em",letterSpacing:-.4},display2:{color:"#818ea3",fontSize:"2.8125rem",fontWeight:400,lineHeight:"1.13333em",marginLeft:"-.02em",letterSpacing:-.4},display3:{color:"#818ea3",fontSize:"3.5rem",fontWeight:400,lineHeight:"1.30357em",marginLeft:"-.02em",letterSpacing:-.4},display4:{fontSize:14,fontWeightLight:300,fontWeightMedium:500,fontWeightRegular:400,letterSpacing:-.4},h1:{color:"rgb(29, 29, 29)",fontSize:"6rem",fontWeight:300,lineHeight:1},h2:{color:"rgb(29, 29, 29)",fontSize:"3.75rem",fontWeight:300,lineHeight:1},h3:{color:"rgb(29, 29, 29)",fontSize:"3rem",fontWeight:400,lineHeight:1.04},h4:{color:"rgb(29, 29, 29)",fontSize:"2.125rem",fontWeight:400,lineHeight:1.17},h5:{color:"rgb(29, 29, 29)",fontSize:"1.5rem",fontWeight:400,lineHeight:1.33,letterSpacing:-.4},h6:{fontSize:"0.8rem",fontWeight:450,lineHeight:"1.71429em",color:"rgba(0, 0, 0, 0.87)",letterSpacing:-.4},subheading:{color:"rgb(29, 29, 29)",fontSize:"1rem",fontWeight:400,lineHeight:"1.5em",letterSpacing:-.4},subtitle1:{color:"rgb(29, 29, 29)",fontSize:"1rem",fontWeight:400,lineHeight:1.75,letterSpacing:-.4},subtitle2:{color:"rgb(29, 29, 29)",fontSize:"0.875rem",fontWeight:500,lineHeight:1.57,letterSpacing:-.4}},shadows:["none","0px 1px 3px 0px rgba(0, 0, 0, 0.1),0px 1px 1px 0px rgba(0, 0, 0, 0.04),0px 2px 1px -1px rgba(0, 0, 0, 0.02)","0px 1px 5px 0px rgba(0, 0, 0, 0.1),0px 2px 2px 0px rgba(0, 0, 0, 0.04),0px 3px 1px -2px rgba(0, 0, 0, 0.02)","0px 1px 8px 0px rgba(0, 0, 0, 0.1),0px 3px 4px 0px rgba(0, 0, 0, 0.04),0px 3px 3px -2px rgba(0, 0, 0, 0.02)","0px 2px 4px -1px rgba(0, 0, 0, 0.1),0px 4px 5px 0px rgba(0, 0, 0, 0.04),0px 1px 10px 0px rgba(0, 0, 0, 0.02)","0px 3px 5px -1px rgba(0, 0, 0, 0.1),0px 5px 8px 0px rgba(0, 0, 0, 0.04),0px 1px 14px 0px rgba(0, 0, 0, 0.02)","0px 3px 5px -1px rgba(0, 0, 0, 0.1),0px 6px 10px 0px rgba(0, 0, 0, 0.04),0px 1px 18px 0px rgba(0, 0, 0, 0.02)","0px 4px 5px -2px rgba(0, 0, 0, 0.1),0px 7px 10px 1px rgba(0, 0, 0, 0.04),0px 2px 16px 1px rgba(0, 0, 0, 0.02)","0px 5px 5px -3px rgba(0, 0, 0, 0.1),0px 8px 10px 1px rgba(0, 0, 0, 0.04),0px 3px 14px 2px rgba(0, 0, 0, 0.02)","0px 5px 6px -3px rgba(0, 0, 0, 0.1),0px 9px 12px 1px rgba(0, 0, 0, 0.04),0px 3px 16px 2px rgba(0, 0, 0, 0.02)","0px 6px 6px -3px rgba(0, 0, 0, 0.1),0px 10px 14px 1px rgba(0, 0, 0, 0.04),0px 4px 18px 3px rgba(0, 0, 0, 0.02)","0px 6px 7px -4px rgba(0, 0, 0, 0.1),0px 11px 15px 1px rgba(0, 0, 0, 0.04),0px 4px 20px 3px rgba(0, 0, 0, 0.02)","0px 7px 8px -4px rgba(0, 0, 0, 0.1),0px 12px 17px 2px rgba(0, 0, 0, 0.04),0px 5px 22px 4px rgba(0, 0, 0, 0.02)","0px 7px 8px -4px rgba(0, 0, 0, 0.1),0px 13px 19px 2px rgba(0, 0, 0, 0.04),0px 5px 24px 4px rgba(0, 0, 0, 0.02)","0px 7px 9px -4px rgba(0, 0, 0, 0.1),0px 14px 21px 2px rgba(0, 0, 0, 0.04),0px 5px 26px 4px rgba(0, 0, 0, 0.02)","0px 8px 9px -5px rgba(0, 0, 0, 0.1),0px 15px 22px 2px rgba(0, 0, 0, 0.04),0px 6px 28px 5px rgba(0, 0, 0, 0.02)","0px 8px 10px -5px rgba(0, 0, 0, 0.1),0px 16px 24px 2px rgba(0, 0, 0, 0.04),0px 6px 30px 5px rgba(0, 0, 0, 0.02)","0px 8px 11px -5px rgba(0, 0, 0, 0.1),0px 17px 26px 2px rgba(0, 0, 0, 0.04),0px 6px 32px 5px rgba(0, 0, 0, 0.02)","0px 9px 11px -5px rgba(0, 0, 0, 0.1),0px 18px 28px 2px rgba(0, 0, 0, 0.04),0px 7px 34px 6px rgba(0, 0, 0, 0.02)","0px 9px 12px -6px rgba(0, 0, 0, 0.1),0px 19px 29px 2px rgba(0, 0, 0, 0.04),0px 7px 36px 6px rgba(0, 0, 0, 0.02)","0px 10px 13px -6px rgba(0, 0, 0, 0.1),0px 20px 31px 3px rgba(0, 0, 0, 0.04),0px 8px 38px 7px rgba(0, 0, 0, 0.02)","0px 10px 13px -6px rgba(0, 0, 0, 0.1),0px 21px 33px 3px rgba(0, 0, 0, 0.04),0px 8px 40px 7px rgba(0, 0, 0, 0.02)","0px 10px 14px -6px rgba(0, 0, 0, 0.1),0px 22px 35px 3px rgba(0, 0, 0, 0.04),0px 8px 42px 7px rgba(0, 0, 0, 0.02)","0px 11px 14px -7px rgba(0, 0, 0, 0.1),0px 23px 36px 3px rgba(0, 0, 0, 0.04),0px 9px 44px 8px rgba(0, 0, 0, 0.02)","0px 11px 15px -7px rgba(0, 0, 0, 0.1),0px 24px 38px 3px rgba(0, 0, 0, 0.04),0px 9px 46px 8px rgba(0, 0, 0, 0.02)",]},u=(0,i.createMuiTheme)(s)},66289(e,t,n){"use strict";function r(e){if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function i(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}function a(){if("undefined"==typeof Reflect||!Reflect.construct||Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(e){return!1}}function o(e,t,n){return(o=a()?Reflect.construct:function(e,t,n){var r=[null];r.push.apply(r,t);var i=new(Function.bind.apply(e,r));return n&&f(i,n.prototype),i}).apply(null,arguments)}function s(e){return(s=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function u(e,t){if("function"!=typeof t&&null!==t)throw TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&f(e,t)}function c(e){return -1!==Function.toString.call(e).indexOf("[native code]")}function l(e,t){return t&&("object"===p(t)||"function"==typeof t)?t:r(e)}function f(e,t){return(f=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}n.d(t,{V0:()=>B,_7:()=>v});var d,h,p=function(e){return e&&"undefined"!=typeof Symbol&&e.constructor===Symbol?"symbol":typeof e};function b(e){var t="function"==typeof Map?new Map:void 0;return(b=function(e){if(null===e||!c(e))return e;if("function"!=typeof e)throw TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,n)}function n(){return o(e,arguments,s(this).constructor)}return n.prototype=Object.create(e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),f(n,e)})(e)}function m(){if("undefined"==typeof Reflect||!Reflect.construct||Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch(e){return!1}}function g(e){var t=m();return function(){var n,r=s(e);if(t){var i=s(this).constructor;n=Reflect.construct(r,arguments,i)}else n=r.apply(this,arguments);return l(this,n)}}var v=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"AuthenticationError(".concat(e.statusText,")"))).errors=[{status:e.status,detail:e},],r}return n}(b(Error)),y=function(e){u(n,e);var t=g(n);function n(e){var r,a=e.errors;return i(this,n),(r=t.call(this,"BadRequestError")).errors=a,r}return n}(b(Error)),w=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"UnprocessableEntityError")).errors=e,r}return n}(b(Error)),_=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"ServerError")).errors=e,r}return n}(b(Error)),E=function(e){u(n,e);var t=g(n);function n(e){var r,a=e.errors;return i(this,n),(r=t.call(this,"ConflictError")).errors=a,r}return n}(b(Error)),S=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"UnknownResponseError(".concat(e.statusText,")"))).errors=[{status:e.status,detail:e.statusText},],r}return n}(b(Error));function k(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:2e4;return Promise.race([fetch(e,t),new Promise(function(e,t){return setTimeout(function(){return t(Error("timeout"))},n)}),])}function x(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]=200&&e.status<300))return[3,2];return[2,e.json()];case 2:if(400!==e.status)return[3,3];return[2,e.json().then(function(e){throw new y(e)})];case 3:if(401!==e.status)return[3,4];throw new v(e);case 4:if(422!==e.status)return[3,6];return[4,$(e)];case 5:throw n=i.sent(),new w(n);case 6:if(409!==e.status)return[3,7];return[2,e.json().then(function(e){throw new E(e)})];case 7:if(!(e.status>=500))return[3,9];return[4,$(e)];case 8:throw r=i.sent(),new _(r);case 9:throw new S(e);case 10:return[2]}})})).apply(this,arguments)}function $(e){return z.apply(this,arguments)}function z(){return(z=j(function(e){return Y(this,function(t){return[2,e.json().then(function(t){return t.errors?t.errors.map(function(t){return{status:e.status,detail:t.detail}}):G(e)}).catch(function(){return G(e)})]})})).apply(this,arguments)}function G(e){return[{status:e.status,detail:e.statusText},]}},50109(e,t,n){"use strict";n.d(t,{LK:()=>o,U2:()=>i,eT:()=>s,t8:()=>a});var r=n(12795);function i(e){return r.ZP.getItem("chainlink.".concat(e))}function a(e,t){r.ZP.setItem("chainlink.".concat(e),t)}function o(e){var t=i(e),n={};if(t)try{return JSON.parse(t)}catch(r){}return n}function s(e,t){a(e,JSON.stringify(t))}},9541(e,t,n){"use strict";n.d(t,{Ks:()=>u,Tp:()=>a,iR:()=>o,pm:()=>s});var r=n(50109),i="persistURL";function a(){return r.U2(i)||""}function o(e){r.t8(i,e)}function s(){return r.LK("authentication")}function u(e){r.eT("authentication",e)}},67121(e,t,n){"use strict";function r(e){var t,n=e.Symbol;return"function"==typeof n?n.observable?t=n.observable:(t=n("observable"),n.observable=t):t="@@observable",t}n.r(t),n.d(t,{default:()=>o}),e=n.hmd(e),i="undefined"!=typeof self?self:"undefined"!=typeof window?window:void 0!==n.g?n.g:e;var i,a=r(i);let o=a},2177(e,t,n){"use strict";n.d(t,{Z:()=>o});var r=!0,i="Invariant failed";function a(e,t){if(!e){if(r)throw Error(i);throw Error(i+": "+(t||""))}}let o=a},11742(e){e.exports=function(){var e=document.getSelection();if(!e.rangeCount)return function(){};for(var t=document.activeElement,n=[],r=0;ru,ZT:()=>i,_T:()=>o,ev:()=>c,mG:()=>s,pi:()=>a});var r=function(e,t){return(r=Object.setPrototypeOf||({__proto__:[]})instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(e,t)};function i(e,t){if("function"!=typeof t&&null!==t)throw TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var a=function(){return(a=Object.assign||function(e){for(var t,n=1,r=arguments.length;nt.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n}function s(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||(n=Promise))(function(n,a){function o(e){try{u(r.next(e))}catch(t){a(t)}}function s(e){try{u(r.throw(e))}catch(t){a(t)}}function u(e){e.done?n(e.value):i(e.value).then(o,s)}u((r=r.apply(e,t||[])).next())})}function u(e,t){var n,r,i,a,o={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return a={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function s(e){return function(t){return u([e,t])}}function u(a){if(n)throw TypeError("Generator is already executing.");for(;o;)try{if(n=1,r&&(i=2&a[0]?r.return:a[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,a[1])).done)return i;switch(r=0,i&&(a=[2&a[0],i.value]),a[0]){case 0:case 1:i=a;break;case 4:return o.label++,{value:a[1],done:!1};case 5:o.label++,r=a[1],a=[0];continue;case 7:a=o.ops.pop(),o.trys.pop();continue;default:if(!(i=(i=o.trys).length>0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]r})},94927(e,t,n){function r(e,t){if(i("noDeprecation"))return e;var n=!1;function r(){if(!n){if(i("throwDeprecation"))throw Error(t);i("traceDeprecation")?console.trace(t):console.warn(t),n=!0}return e.apply(this,arguments)}return r}function i(e){try{if(!n.g.localStorage)return!1}catch(t){return!1}var r=n.g.localStorage[e];return null!=r&&"true"===String(r).toLowerCase()}e.exports=r},42473(e){"use strict";var t=function(){};e.exports=t},84763(e){e.exports=Worker},47529(e){e.exports=n;var t=Object.prototype.hasOwnProperty;function n(){for(var e={},n=0;ne.length)&&(t=e.length);for(var n=0,r=Array(t);n=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}e.exports=i,e.exports.__esModule=!0,e.exports.default=e.exports},7071(e){function t(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},94993(e,t,n){var r=n(18698).default,i=n(66115);function a(e,t){if(t&&("object"===r(t)||"function"==typeof t))return t;if(void 0!==t)throw TypeError("Derived constructors may only return object or undefined");return i(e)}e.exports=a,e.exports.__esModule=!0,e.exports.default=e.exports},6015(e){function t(n,r){return e.exports=t=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},e.exports.__esModule=!0,e.exports.default=e.exports,t(n,r)}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},861(e,t,n){var r=n(63405),i=n(79498),a=n(86116),o=n(42281);function s(e){return r(e)||i(e)||a(e)||o()}e.exports=s,e.exports.__esModule=!0,e.exports.default=e.exports},18698(e){function t(n){return e.exports=t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e.exports.__esModule=!0,e.exports.default=e.exports,t(n)}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},86116(e,t,n){var r=n(73897);function i(e,t){if(e){if("string"==typeof e)return r(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);if("Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return r(e,t)}}e.exports=i,e.exports.__esModule=!0,e.exports.default=e.exports},1644(e,t,n){"use strict";var r,i;function a(e){return!!e&&e<7}n.d(t,{I:()=>r,O:()=>a}),(i=r||(r={}))[i.loading=1]="loading",i[i.setVariables=2]="setVariables",i[i.fetchMore=3]="fetchMore",i[i.refetch=4]="refetch",i[i.poll=6]="poll",i[i.ready=7]="ready",i[i.error=8]="error"},30990(e,t,n){"use strict";n.d(t,{MS:()=>s,YG:()=>a,cA:()=>c,ls:()=>o});var r=n(70655);n(83952);var i=n(13154),a=Symbol();function o(e){return!!e.extensions&&Array.isArray(e.extensions[a])}function s(e){return e.hasOwnProperty("graphQLErrors")}var u=function(e){var t=(0,r.ev)((0,r.ev)((0,r.ev)([],e.graphQLErrors,!0),e.clientErrors,!0),e.protocolErrors,!0);return e.networkError&&t.push(e.networkError),t.map(function(e){return(0,i.s)(e)&&e.message||"Error message not found."}).join("\n")},c=function(e){function t(n){var r=n.graphQLErrors,i=n.protocolErrors,a=n.clientErrors,o=n.networkError,s=n.errorMessage,c=n.extraInfo,l=e.call(this,s)||this;return l.name="ApolloError",l.graphQLErrors=r||[],l.protocolErrors=i||[],l.clientErrors=a||[],l.networkError=o||null,l.message=s||u(l),l.extraInfo=c,l.__proto__=t.prototype,l}return(0,r.ZT)(t,e),t}(Error)},85317(e,t,n){"use strict";n.d(t,{K:()=>a});var r=n(67294),i=n(30320).aS?Symbol.for("__APOLLO_CONTEXT__"):"__APOLLO_CONTEXT__";function a(){var e=r.createContext[i];return e||(Object.defineProperty(r.createContext,i,{value:e=r.createContext({}),enumerable:!1,writable:!1,configurable:!0}),e.displayName="ApolloContext"),e}},21436(e,t,n){"use strict";n.d(t,{O:()=>i,k:()=>r});var r=Array.isArray;function i(e){return Array.isArray(e)&&e.length>0}},30320(e,t,n){"use strict";n.d(t,{DN:()=>s,JC:()=>l,aS:()=>o,mr:()=>i,sy:()=>a});var r=n(83952),i="function"==typeof WeakMap&&"ReactNative"!==(0,r.wY)(function(){return navigator.product}),a="function"==typeof WeakSet,o="function"==typeof Symbol&&"function"==typeof Symbol.for,s=o&&Symbol.asyncIterator,u="function"==typeof(0,r.wY)(function(){return window.document.createElement}),c=(0,r.wY)(function(){return navigator.userAgent.indexOf("jsdom")>=0})||!1,l=u&&!c},53712(e,t,n){"use strict";function r(){for(var e=[],t=0;tr})},10542(e,t,n){"use strict";n.d(t,{J:()=>o}),n(83952);var r=n(13154);function i(e){var t=new Set([e]);return t.forEach(function(e){(0,r.s)(e)&&a(e)===e&&Object.getOwnPropertyNames(e).forEach(function(n){(0,r.s)(e[n])&&t.add(e[n])})}),e}function a(e){if(__DEV__&&!Object.isFrozen(e))try{Object.freeze(e)}catch(t){if(t instanceof TypeError)return null;throw t}return e}function o(e){return __DEV__&&i(e),e}},14012(e,t,n){"use strict";n.d(t,{J:()=>a});var r=n(70655),i=n(53712);function a(e,t){return(0,i.o)(e,t,t.variables&&{variables:(0,r.pi)((0,r.pi)({},e&&e.variables),t.variables)})}},13154(e,t,n){"use strict";function r(e){return null!==e&&"object"==typeof e}n.d(t,{s:()=>r})},83952(e,t,n){"use strict";n.d(t,{ej:()=>u,kG:()=>c,wY:()=>h});var r,i=n(70655),a="Invariant Violation",o=Object.setPrototypeOf,s=void 0===o?function(e,t){return e.__proto__=t,e}:o,u=function(e){function t(n){void 0===n&&(n=a);var r=e.call(this,"number"==typeof n?a+": "+n+" (see https://github.com/apollographql/invariant-packages)":n)||this;return r.framesToPop=1,r.name=a,s(r,t.prototype),r}return(0,i.ZT)(t,e),t}(Error);function c(e,t){if(!e)throw new u(t)}var l=["debug","log","warn","error","silent"],f=l.indexOf("log");function d(e){return function(){if(l.indexOf(e)>=f)return(console[e]||console.log).apply(console,arguments)}}function h(e){try{return e()}catch(t){}}(r=c||(c={})).debug=d("debug"),r.log=d("log"),r.warn=d("warn"),r.error=d("error");let p=h(function(){return globalThis})||h(function(){return window})||h(function(){return self})||h(function(){return global})||h(function(){return h.constructor("return this")()});var b="__",m=[b,b].join("DEV");function g(){try{return Boolean(__DEV__)}catch(e){return Object.defineProperty(p,m,{value:"production"!==h(function(){return"production"}),enumerable:!1,configurable:!0,writable:!0}),p[m]}}let v=g();function y(e){try{return e()}catch(t){}}var w=y(function(){return globalThis})||y(function(){return window})||y(function(){return self})||y(function(){return global})||y(function(){return y.constructor("return this")()}),_=!1;function E(){!w||y(function(){return"production"})||y(function(){return process})||(Object.defineProperty(w,"process",{value:{env:{NODE_ENV:"production"}},configurable:!0,enumerable:!1,writable:!0}),_=!0)}function S(){_&&(delete w.process,_=!1)}E();var k=n(10143);function x(){return k.H,S()}function T(){__DEV__?c("boolean"==typeof v,v):c("boolean"==typeof v,39)}x(),T()},4942(e,t,n){"use strict";function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}n.d(t,{Z:()=>r})},87462(e,t,n){"use strict";function r(){return(r=Object.assign?Object.assign.bind():function(e){for(var t=1;tr})},51721(e,t,n){"use strict";function r(e,t){return(r=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e})(e,t)}function i(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,r(e,t)}n.d(t,{Z:()=>i})},63366(e,t,n){"use strict";function r(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}n.d(t,{Z:()=>r})},25821(e,t,n){"use strict";n.d(t,{Z:()=>s});var r=n(45695);function i(e){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var a=10,o=2;function s(e){return u(e,[])}function u(e,t){switch(i(e)){case"string":return JSON.stringify(e);case"function":return e.name?"[function ".concat(e.name,"]"):"[function]";case"object":if(null===e)return"null";return c(e,t);default:return String(e)}}function c(e,t){if(-1!==t.indexOf(e))return"[Circular]";var n=[].concat(t,[e]),r=d(e);if(void 0!==r){var i=r.call(e);if(i!==e)return"string"==typeof i?i:u(i,n)}else if(Array.isArray(e))return f(e,n);return l(e,n)}function l(e,t){var n=Object.keys(e);return 0===n.length?"{}":t.length>o?"["+h(e)+"]":"{ "+n.map(function(n){var r=u(e[n],t);return n+": "+r}).join(", ")+" }"}function f(e,t){if(0===e.length)return"[]";if(t.length>o)return"[Array]";for(var n=Math.min(a,e.length),r=e.length-n,i=[],s=0;s1&&i.push("... ".concat(r," more items")),"["+i.join(", ")+"]"}function d(e){var t=e[String(r.Z)];return"function"==typeof t?t:"function"==typeof e.inspect?e.inspect:void 0}function h(e){var t=Object.prototype.toString.call(e).replace(/^\[object /,"").replace(/]$/,"");if("Object"===t&&"function"==typeof e.constructor){var n=e.constructor.name;if("string"==typeof n&&""!==n)return n}return t}},45695(e,t,n){"use strict";n.d(t,{Z:()=>i});var r="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):void 0;let i=r},25217(e,t,n){"use strict";function r(e,t){if(!Boolean(e))throw Error(null!=t?t:"Unexpected invariant triggered.")}n.d(t,{Ye:()=>o,WU:()=>s,UG:()=>u});var i=n(45695);function a(e){var t=e.prototype.toJSON;"function"==typeof t||r(0),e.prototype.inspect=t,i.Z&&(e.prototype[i.Z]=t)}var o=function(){function e(e,t,n){this.start=e.start,this.end=t.end,this.startToken=e,this.endToken=t,this.source=n}return e.prototype.toJSON=function(){return{start:this.start,end:this.end}},e}();a(o);var s=function(){function e(e,t,n,r,i,a,o){this.kind=e,this.start=t,this.end=n,this.line=r,this.column=i,this.value=o,this.prev=a,this.next=null}return e.prototype.toJSON=function(){return{kind:this.kind,value:this.value,line:this.line,column:this.column}},e}();function u(e){return null!=e&&"string"==typeof e.kind}a(s)},87392(e,t,n){"use strict";function r(e){var t=e.split(/\r\n|[\n\r]/g),n=a(e);if(0!==n)for(var r=1;ro&&i(t[s-1]);)--s;return t.slice(o,s).join("\n")}function i(e){for(var t=0;t1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],r=-1===e.indexOf("\n"),i=" "===e[0]||" "===e[0],a='"'===e[e.length-1],o="\\"===e[e.length-1],s=!r||a||o||n,u="";return s&&!(r&&i)&&(u+="\n"+t),u+=t?e.replace(/\n/g,"\n"+t):e,s&&(u+="\n"),'"""'+u.replace(/"""/g,'\\"""')+'"""'}n.d(t,{LZ:()=>o,W7:()=>r})},97359(e,t,n){"use strict";n.d(t,{h:()=>r});var r=Object.freeze({NAME:"Name",DOCUMENT:"Document",OPERATION_DEFINITION:"OperationDefinition",VARIABLE_DEFINITION:"VariableDefinition",SELECTION_SET:"SelectionSet",FIELD:"Field",ARGUMENT:"Argument",FRAGMENT_SPREAD:"FragmentSpread",INLINE_FRAGMENT:"InlineFragment",FRAGMENT_DEFINITION:"FragmentDefinition",VARIABLE:"Variable",INT:"IntValue",FLOAT:"FloatValue",STRING:"StringValue",BOOLEAN:"BooleanValue",NULL:"NullValue",ENUM:"EnumValue",LIST:"ListValue",OBJECT:"ObjectValue",OBJECT_FIELD:"ObjectField",DIRECTIVE:"Directive",NAMED_TYPE:"NamedType",LIST_TYPE:"ListType",NON_NULL_TYPE:"NonNullType",SCHEMA_DEFINITION:"SchemaDefinition",OPERATION_TYPE_DEFINITION:"OperationTypeDefinition",SCALAR_TYPE_DEFINITION:"ScalarTypeDefinition",OBJECT_TYPE_DEFINITION:"ObjectTypeDefinition",FIELD_DEFINITION:"FieldDefinition",INPUT_VALUE_DEFINITION:"InputValueDefinition",INTERFACE_TYPE_DEFINITION:"InterfaceTypeDefinition",UNION_TYPE_DEFINITION:"UnionTypeDefinition",ENUM_TYPE_DEFINITION:"EnumTypeDefinition",ENUM_VALUE_DEFINITION:"EnumValueDefinition",INPUT_OBJECT_TYPE_DEFINITION:"InputObjectTypeDefinition",DIRECTIVE_DEFINITION:"DirectiveDefinition",SCHEMA_EXTENSION:"SchemaExtension",SCALAR_TYPE_EXTENSION:"ScalarTypeExtension",OBJECT_TYPE_EXTENSION:"ObjectTypeExtension",INTERFACE_TYPE_EXTENSION:"InterfaceTypeExtension",UNION_TYPE_EXTENSION:"UnionTypeExtension",ENUM_TYPE_EXTENSION:"EnumTypeExtension",INPUT_OBJECT_TYPE_EXTENSION:"InputObjectTypeExtension"})},10143(e,t,n){"use strict";n.d(t,{H:()=>c,T:()=>l});var r=n(99763),i=n(25821);function a(e,t){if(!Boolean(e))throw Error(t)}let o=function(e,t){return e instanceof t};function s(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:"GraphQL request",n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{line:1,column:1};"string"==typeof e||a(0,"Body must be a string. Received: ".concat((0,i.Z)(e),".")),this.body=e,this.name=t,this.locationOffset=n,this.locationOffset.line>0||a(0,"line in locationOffset is 1-indexed and must be positive."),this.locationOffset.column>0||a(0,"column in locationOffset is 1-indexed and must be positive.")}return u(e,[{key:r.YF,get:function(){return"Source"}}]),e}();function l(e){return o(e,c)}},99763(e,t,n){"use strict";n.d(t,{YF:()=>r});var r="function"==typeof Symbol&&null!=Symbol.toStringTag?Symbol.toStringTag:"@@toStringTag"},37452(e){"use strict";e.exports=JSON.parse('{"AElig":"\xc6","AMP":"&","Aacute":"\xc1","Acirc":"\xc2","Agrave":"\xc0","Aring":"\xc5","Atilde":"\xc3","Auml":"\xc4","COPY":"\xa9","Ccedil":"\xc7","ETH":"\xd0","Eacute":"\xc9","Ecirc":"\xca","Egrave":"\xc8","Euml":"\xcb","GT":">","Iacute":"\xcd","Icirc":"\xce","Igrave":"\xcc","Iuml":"\xcf","LT":"<","Ntilde":"\xd1","Oacute":"\xd3","Ocirc":"\xd4","Ograve":"\xd2","Oslash":"\xd8","Otilde":"\xd5","Ouml":"\xd6","QUOT":"\\"","REG":"\xae","THORN":"\xde","Uacute":"\xda","Ucirc":"\xdb","Ugrave":"\xd9","Uuml":"\xdc","Yacute":"\xdd","aacute":"\xe1","acirc":"\xe2","acute":"\xb4","aelig":"\xe6","agrave":"\xe0","amp":"&","aring":"\xe5","atilde":"\xe3","auml":"\xe4","brvbar":"\xa6","ccedil":"\xe7","cedil":"\xb8","cent":"\xa2","copy":"\xa9","curren":"\xa4","deg":"\xb0","divide":"\xf7","eacute":"\xe9","ecirc":"\xea","egrave":"\xe8","eth":"\xf0","euml":"\xeb","frac12":"\xbd","frac14":"\xbc","frac34":"\xbe","gt":">","iacute":"\xed","icirc":"\xee","iexcl":"\xa1","igrave":"\xec","iquest":"\xbf","iuml":"\xef","laquo":"\xab","lt":"<","macr":"\xaf","micro":"\xb5","middot":"\xb7","nbsp":"\xa0","not":"\xac","ntilde":"\xf1","oacute":"\xf3","ocirc":"\xf4","ograve":"\xf2","ordf":"\xaa","ordm":"\xba","oslash":"\xf8","otilde":"\xf5","ouml":"\xf6","para":"\xb6","plusmn":"\xb1","pound":"\xa3","quot":"\\"","raquo":"\xbb","reg":"\xae","sect":"\xa7","shy":"\xad","sup1":"\xb9","sup2":"\xb2","sup3":"\xb3","szlig":"\xdf","thorn":"\xfe","times":"\xd7","uacute":"\xfa","ucirc":"\xfb","ugrave":"\xf9","uml":"\xa8","uuml":"\xfc","yacute":"\xfd","yen":"\xa5","yuml":"\xff"}')},93580(e){"use strict";e.exports=JSON.parse('{"0":"�","128":"€","130":"‚","131":"ƒ","132":"„","133":"…","134":"†","135":"‡","136":"ˆ","137":"‰","138":"Š","139":"‹","140":"Œ","142":"Ž","145":"‘","146":"’","147":"“","148":"”","149":"•","150":"–","151":"—","152":"˜","153":"™","154":"š","155":"›","156":"œ","158":"ž","159":"Ÿ"}')},67946(e){"use strict";e.exports=JSON.parse('{"locale":"en","long":{"year":{"previous":"last year","current":"this year","next":"next year","past":{"one":"{0} year ago","other":"{0} years ago"},"future":{"one":"in {0} year","other":"in {0} years"}},"quarter":{"previous":"last quarter","current":"this quarter","next":"next quarter","past":{"one":"{0} quarter ago","other":"{0} quarters ago"},"future":{"one":"in {0} quarter","other":"in {0} quarters"}},"month":{"previous":"last month","current":"this month","next":"next month","past":{"one":"{0} month ago","other":"{0} months ago"},"future":{"one":"in {0} month","other":"in {0} months"}},"week":{"previous":"last week","current":"this week","next":"next week","past":{"one":"{0} week ago","other":"{0} weeks ago"},"future":{"one":"in {0} week","other":"in {0} weeks"}},"day":{"previous":"yesterday","current":"today","next":"tomorrow","past":{"one":"{0} day ago","other":"{0} days ago"},"future":{"one":"in {0} day","other":"in {0} days"}},"hour":{"current":"this hour","past":{"one":"{0} hour ago","other":"{0} hours ago"},"future":{"one":"in {0} hour","other":"in {0} hours"}},"minute":{"current":"this minute","past":{"one":"{0} minute ago","other":"{0} minutes ago"},"future":{"one":"in {0} minute","other":"in {0} minutes"}},"second":{"current":"now","past":{"one":"{0} second ago","other":"{0} seconds ago"},"future":{"one":"in {0} second","other":"in {0} seconds"}}},"short":{"year":{"previous":"last yr.","current":"this yr.","next":"next yr.","past":"{0} yr. ago","future":"in {0} yr."},"quarter":{"previous":"last qtr.","current":"this qtr.","next":"next qtr.","past":{"one":"{0} qtr. ago","other":"{0} qtrs. ago"},"future":{"one":"in {0} qtr.","other":"in {0} qtrs."}},"month":{"previous":"last mo.","current":"this mo.","next":"next mo.","past":"{0} mo. ago","future":"in {0} mo."},"week":{"previous":"last wk.","current":"this wk.","next":"next wk.","past":"{0} wk. ago","future":"in {0} wk."},"day":{"previous":"yesterday","current":"today","next":"tomorrow","past":{"one":"{0} day ago","other":"{0} days ago"},"future":{"one":"in {0} day","other":"in {0} days"}},"hour":{"current":"this hour","past":"{0} hr. ago","future":"in {0} hr."},"minute":{"current":"this minute","past":"{0} min. ago","future":"in {0} min."},"second":{"current":"now","past":"{0} sec. ago","future":"in {0} sec."}},"narrow":{"year":{"previous":"last yr.","current":"this yr.","next":"next yr.","past":"{0} yr. ago","future":"in {0} yr."},"quarter":{"previous":"last qtr.","current":"this qtr.","next":"next qtr.","past":{"one":"{0} qtr. ago","other":"{0} qtrs. ago"},"future":{"one":"in {0} qtr.","other":"in {0} qtrs."}},"month":{"previous":"last mo.","current":"this mo.","next":"next mo.","past":"{0} mo. ago","future":"in {0} mo."},"week":{"previous":"last wk.","current":"this wk.","next":"next wk.","past":"{0} wk. ago","future":"in {0} wk."},"day":{"previous":"yesterday","current":"today","next":"tomorrow","past":{"one":"{0} day ago","other":"{0} days ago"},"future":{"one":"in {0} day","other":"in {0} days"}},"hour":{"current":"this hour","past":"{0} hr. ago","future":"in {0} hr."},"minute":{"current":"this minute","past":"{0} min. ago","future":"in {0} min."},"second":{"current":"now","past":"{0} sec. ago","future":"in {0} sec."}},"now":{"now":{"current":"now","future":"in a moment","past":"just now"}},"mini":{"year":"{0}yr","month":"{0}mo","week":"{0}wk","day":"{0}d","hour":"{0}h","minute":"{0}m","second":"{0}s","now":"now"},"short-time":{"year":"{0} yr.","month":"{0} mo.","week":"{0} wk.","day":{"one":"{0} day","other":"{0} days"},"hour":"{0} hr.","minute":"{0} min.","second":"{0} sec."},"long-time":{"year":{"one":"{0} year","other":"{0} years"},"month":{"one":"{0} month","other":"{0} months"},"week":{"one":"{0} week","other":"{0} weeks"},"day":{"one":"{0} day","other":"{0} days"},"hour":{"one":"{0} hour","other":"{0} hours"},"minute":{"one":"{0} minute","other":"{0} minutes"},"second":{"one":"{0} second","other":"{0} seconds"}}}')}},__webpack_module_cache__={};function __webpack_require__(e){var t=__webpack_module_cache__[e];if(void 0!==t)return t.exports;var n=__webpack_module_cache__[e]={id:e,loaded:!1,exports:{}};return __webpack_modules__[e].call(n.exports,n,n.exports,__webpack_require__),n.loaded=!0,n.exports}__webpack_require__.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return __webpack_require__.d(t,{a:t}),t},(()=>{var e,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__;__webpack_require__.t=function(n,r){if(1&r&&(n=this(n)),8&r||"object"==typeof n&&n&&(4&r&&n.__esModule||16&r&&"function"==typeof n.then))return n;var i=Object.create(null);__webpack_require__.r(i);var a={};e=e||[null,t({}),t([]),t(t)];for(var o=2&r&&n;"object"==typeof o&&!~e.indexOf(o);o=t(o))Object.getOwnPropertyNames(o).forEach(e=>a[e]=()=>n[e]);return a.default=()=>n,__webpack_require__.d(i,a),i}})(),__webpack_require__.d=(e,t)=>{for(var n in t)__webpack_require__.o(t,n)&&!__webpack_require__.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},__webpack_require__.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),__webpack_require__.hmd=e=>((e=Object.create(e)).children||(e.children=[]),Object.defineProperty(e,"exports",{enumerable:!0,set(){throw Error("ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: "+e.id)}}),e),__webpack_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),__webpack_require__.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},__webpack_require__.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),__webpack_require__.p="/assets/",__webpack_require__.nc=void 0;var __webpack_exports__={};(()=>{"use strict";var e,t,n,r,i=__webpack_require__(32316),a=__webpack_require__(8126),o=__webpack_require__(5690),s=__webpack_require__(30381),u=__webpack_require__.n(s),c=__webpack_require__(67294),l=__webpack_require__(73935),f=__webpack_require__.n(l),d=__webpack_require__(57209),h=__webpack_require__(37703),p=__webpack_require__(97779),b=__webpack_require__(28500);function m(e){return function(t){var n=t.dispatch,r=t.getState;return function(t){return function(i){return"function"==typeof i?i(n,r,e):t(i)}}}}var g=m();g.withExtraArgument=m;let v=g;var y=__webpack_require__(76489);function w(e){return function(t){return function(n){return function(r){n(r);var i=e||document&&document.cookie||"",a=t.getState();if("MATCH_ROUTE"===r.type&&"/signin"!==a.notifications.currentUrl){var o=(0,y.Q)(i);if(o.explorer)try{var s=JSON.parse(o.explorer);if("error"===s.status){var u=_(s.url);n({type:"NOTIFY_ERROR_MSG",msg:u})}}catch(c){n({type:"NOTIFY_ERROR_MSG",msg:"Invalid explorer status"})}}}}}}function _(e){var t="Can't connect to explorer: ".concat(e);return e.match(/^wss?:.+/)?t:"".concat(t,". You must use a websocket.")}var E=__webpack_require__(16353);function S(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}}}throw TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function ei(e,t){if(e){if("string"==typeof e)return ea(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);if("Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return ea(e,t)}}function ea(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n1,i=!1,a=arguments[1],o=a;return new n(function(n){return t.subscribe({next:function(t){var a=!i;if(i=!0,!a||r)try{o=e(o,t)}catch(s){return n.error(s)}else o=t},error:function(e){n.error(e)},complete:function(){if(!i&&!r)return n.error(TypeError("Cannot reduce an empty sequence"));n.next(o),n.complete()}})})},t.concat=function(){for(var e=this,t=arguments.length,n=Array(t),r=0;r=0&&i.splice(e,1),o()}});i.push(s)},error:function(e){r.error(e)},complete:function(){o()}});function o(){a.closed&&0===i.length&&r.complete()}return function(){i.forEach(function(e){return e.unsubscribe()}),a.unsubscribe()}})},t[ed]=function(){return this},e.from=function(t){var n="function"==typeof this?this:e;if(null==t)throw TypeError(t+" is not an object");var r=ep(t,ed);if(r){var i=r.call(t);if(Object(i)!==i)throw TypeError(i+" is not an object");return em(i)&&i.constructor===n?i:new n(function(e){return i.subscribe(e)})}if(ec("iterator")&&(r=ep(t,ef)))return new n(function(e){ev(function(){if(!e.closed){for(var n,i=er(r.call(t));!(n=i()).done;){var a=n.value;if(e.next(a),e.closed)return}e.complete()}})});if(Array.isArray(t))return new n(function(e){ev(function(){if(!e.closed){for(var n=0;n0))return n.connection.key;var r=n.connection.filter?n.connection.filter:[];r.sort();var i={};return r.forEach(function(e){i[e]=t[e]}),"".concat(n.connection.key,"(").concat(eV(i),")")}var a=e;if(t){var o=eV(t);a+="(".concat(o,")")}return n&&Object.keys(n).forEach(function(e){-1===eW.indexOf(e)&&(n[e]&&Object.keys(n[e]).length?a+="@".concat(e,"(").concat(eV(n[e]),")"):a+="@".concat(e))}),a},{setStringify:function(e){var t=eV;return eV=e,t}}),eV=function(e){return JSON.stringify(e,eq)};function eq(e,t){return(0,eO.s)(t)&&!Array.isArray(t)&&(t=Object.keys(t).sort().reduce(function(e,n){return e[n]=t[n],e},{})),t}function eZ(e,t){if(e.arguments&&e.arguments.length){var n={};return e.arguments.forEach(function(e){var r;return ez(n,e.name,e.value,t)}),n}return null}function eX(e){return e.alias?e.alias.value:e.name.value}function eJ(e,t,n){for(var r,i=0,a=t.selections;it.indexOf(i))throw __DEV__?new Q.ej("illegal argument: ".concat(i)):new Q.ej(27)}return e}function tt(e,t){return t?t(e):eT.of()}function tn(e){return"function"==typeof e?new ta(e):e}function tr(e){return e.request.length<=1}var ti=function(e){function t(t,n){var r=e.call(this,t)||this;return r.link=n,r}return(0,en.ZT)(t,e),t}(Error),ta=function(){function e(e){e&&(this.request=e)}return e.empty=function(){return new e(function(){return eT.of()})},e.from=function(t){return 0===t.length?e.empty():t.map(tn).reduce(function(e,t){return e.concat(t)})},e.split=function(t,n,r){var i=tn(n),a=tn(r||new e(tt));return new e(tr(i)&&tr(a)?function(e){return t(e)?i.request(e)||eT.of():a.request(e)||eT.of()}:function(e,n){return t(e)?i.request(e,n)||eT.of():a.request(e,n)||eT.of()})},e.execute=function(e,t){return e.request(eM(t.context,e7(te(t))))||eT.of()},e.concat=function(t,n){var r=tn(t);if(tr(r))return __DEV__&&Q.kG.warn(new ti("You are calling concat on a terminating link, which will have no effect",r)),r;var i=tn(n);return new e(tr(i)?function(e){return r.request(e,function(e){return i.request(e)||eT.of()})||eT.of()}:function(e,t){return r.request(e,function(e){return i.request(e,t)||eT.of()})||eT.of()})},e.prototype.split=function(t,n,r){return this.concat(e.split(t,n,r||new e(tt)))},e.prototype.concat=function(t){return e.concat(this,t)},e.prototype.request=function(e,t){throw __DEV__?new Q.ej("request is not implemented"):new Q.ej(22)},e.prototype.onError=function(e,t){if(t&&t.error)return t.error(e),!1;throw e},e.prototype.setOnError=function(e){return this.onError=e,this},e}(),to=__webpack_require__(25821),ts=__webpack_require__(25217),tu={Name:[],Document:["definitions"],OperationDefinition:["name","variableDefinitions","directives","selectionSet"],VariableDefinition:["variable","type","defaultValue","directives"],Variable:["name"],SelectionSet:["selections"],Field:["alias","name","arguments","directives","selectionSet"],Argument:["name","value"],FragmentSpread:["name","directives"],InlineFragment:["typeCondition","directives","selectionSet"],FragmentDefinition:["name","variableDefinitions","typeCondition","directives","selectionSet"],IntValue:[],FloatValue:[],StringValue:[],BooleanValue:[],NullValue:[],EnumValue:[],ListValue:["values"],ObjectValue:["fields"],ObjectField:["name","value"],Directive:["name","arguments"],NamedType:["name"],ListType:["type"],NonNullType:["type"],SchemaDefinition:["description","directives","operationTypes"],OperationTypeDefinition:["type"],ScalarTypeDefinition:["description","name","directives"],ObjectTypeDefinition:["description","name","interfaces","directives","fields"],FieldDefinition:["description","name","arguments","type","directives"],InputValueDefinition:["description","name","type","defaultValue","directives"],InterfaceTypeDefinition:["description","name","interfaces","directives","fields"],UnionTypeDefinition:["description","name","directives","types"],EnumTypeDefinition:["description","name","directives","values"],EnumValueDefinition:["description","name","directives"],InputObjectTypeDefinition:["description","name","directives","fields"],DirectiveDefinition:["description","name","arguments","locations"],SchemaExtension:["directives","operationTypes"],ScalarTypeExtension:["name","directives"],ObjectTypeExtension:["name","interfaces","directives","fields"],InterfaceTypeExtension:["name","interfaces","directives","fields"],UnionTypeExtension:["name","directives","types"],EnumTypeExtension:["name","directives","values"],InputObjectTypeExtension:["name","directives","fields"]},tc=Object.freeze({});function tl(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:tu,r=void 0,i=Array.isArray(e),a=[e],o=-1,s=[],u=void 0,c=void 0,l=void 0,f=[],d=[],h=e;do{var p,b=++o===a.length,m=b&&0!==s.length;if(b){if(c=0===d.length?void 0:f[f.length-1],u=l,l=d.pop(),m){if(i)u=u.slice();else{for(var g={},v=0,y=Object.keys(u);v1)for(var r=new tB,i=1;i=0;--a){var o=i[a],s=isNaN(+o)?{}:[];s[o]=t,t=s}n=r.merge(n,t)}),n}var tW=Object.prototype.hasOwnProperty;function tK(e,t){var n,r,i,a,o;return(0,en.mG)(this,void 0,void 0,function(){var s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T,M,O,A;return(0,en.Jh)(this,function(L){switch(L.label){case 0:if(void 0===TextDecoder)throw Error("TextDecoder must be defined in the environment: please import a polyfill.");s=new TextDecoder("utf-8"),u=null===(n=e.headers)||void 0===n?void 0:n.get("content-type"),c="boundary=",l=(null==u?void 0:u.includes(c))?null==u?void 0:u.substring((null==u?void 0:u.indexOf(c))+c.length).replace(/['"]/g,"").replace(/\;(.*)/gm,"").trim():"-",f="\r\n--".concat(l),d="",h=tI(e),p=!0,L.label=1;case 1:if(!p)return[3,3];return[4,h.next()];case 2:for(m=(b=L.sent()).value,g=b.done,v="string"==typeof m?m:s.decode(m),y=d.length-f.length+1,p=!g,d+=v,w=d.indexOf(f,y);w>-1;){if(_=void 0,_=(O=[d.slice(0,w),d.slice(w+f.length),])[0],d=O[1],E=_.indexOf("\r\n\r\n"),(k=(S=tV(_.slice(0,E)))["content-type"])&&-1===k.toLowerCase().indexOf("application/json"))throw Error("Unsupported patch content type: application/json is required.");if(x=_.slice(E))try{T=tq(e,x),Object.keys(T).length>1||"data"in T||"incremental"in T||"errors"in T||"payload"in T?tz(T)?(M={},"payload"in T&&(M=(0,en.pi)({},T.payload)),"errors"in T&&(M=(0,en.pi)((0,en.pi)({},M),{extensions:(0,en.pi)((0,en.pi)({},"extensions"in M?M.extensions:null),((A={})[tN.YG]=T.errors,A))})),null===(r=t.next)||void 0===r||r.call(t,M)):null===(i=t.next)||void 0===i||i.call(t,T):1===Object.keys(T).length&&"hasNext"in T&&!T.hasNext&&(null===(a=t.complete)||void 0===a||a.call(t))}catch(C){tZ(C,t)}w=d.indexOf(f)}return[3,1];case 3:return null===(o=t.complete)||void 0===o||o.call(t),[2]}})})}function tV(e){var t={};return e.split("\n").forEach(function(e){var n=e.indexOf(":");if(n>-1){var r=e.slice(0,n).trim().toLowerCase(),i=e.slice(n+1).trim();t[r]=i}}),t}function tq(e,t){e.status>=300&&tD(e,function(){try{return JSON.parse(t)}catch(e){return t}}(),"Response not successful: Received status code ".concat(e.status));try{return JSON.parse(t)}catch(n){var r=n;throw r.name="ServerParseError",r.response=e,r.statusCode=e.status,r.bodyText=t,r}}function tZ(e,t){var n,r;"AbortError"!==e.name&&(e.result&&e.result.errors&&e.result.data&&(null===(n=t.next)||void 0===n||n.call(t,e.result)),null===(r=t.error)||void 0===r||r.call(t,e))}function tX(e,t,n){tJ(t)(e).then(function(e){var t,r;null===(t=n.next)||void 0===t||t.call(n,e),null===(r=n.complete)||void 0===r||r.call(n)}).catch(function(e){return tZ(e,n)})}function tJ(e){return function(t){return t.text().then(function(e){return tq(t,e)}).then(function(n){return t.status>=300&&tD(t,n,"Response not successful: Received status code ".concat(t.status)),Array.isArray(n)||tW.call(n,"data")||tW.call(n,"errors")||tD(t,n,"Server response was missing for query '".concat(Array.isArray(e)?e.map(function(e){return e.operationName}):e.operationName,"'.")),n})}}var tQ=function(e){if(!e&&"undefined"==typeof fetch)throw __DEV__?new Q.ej("\n\"fetch\" has not been found globally and no fetcher has been configured. To fix this, install a fetch package (like https://www.npmjs.com/package/cross-fetch), instantiate the fetcher, and pass it into your HttpLink constructor. For example:\n\nimport fetch from 'cross-fetch';\nimport { ApolloClient, HttpLink } from '@apollo/client';\nconst client = new ApolloClient({\n link: new HttpLink({ uri: '/graphql', fetch })\n});\n "):new Q.ej(23)},t1=__webpack_require__(87392);function t0(e){return tl(e,{leave:t3})}var t2=80,t3={Name:function(e){return e.value},Variable:function(e){return"$"+e.name},Document:function(e){return t6(e.definitions,"\n\n")+"\n"},OperationDefinition:function(e){var t=e.operation,n=e.name,r=t8("(",t6(e.variableDefinitions,", "),")"),i=t6(e.directives," "),a=e.selectionSet;return n||i||r||"query"!==t?t6([t,t6([n,r]),i,a]," "):a},VariableDefinition:function(e){var t=e.variable,n=e.type,r=e.defaultValue,i=e.directives;return t+": "+n+t8(" = ",r)+t8(" ",t6(i," "))},SelectionSet:function(e){return t5(e.selections)},Field:function(e){var t=e.alias,n=e.name,r=e.arguments,i=e.directives,a=e.selectionSet,o=t8("",t,": ")+n,s=o+t8("(",t6(r,", "),")");return s.length>t2&&(s=o+t8("(\n",t9(t6(r,"\n")),"\n)")),t6([s,t6(i," "),a]," ")},Argument:function(e){var t;return e.name+": "+e.value},FragmentSpread:function(e){var t;return"..."+e.name+t8(" ",t6(e.directives," "))},InlineFragment:function(e){var t=e.typeCondition,n=e.directives,r=e.selectionSet;return t6(["...",t8("on ",t),t6(n," "),r]," ")},FragmentDefinition:function(e){var t=e.name,n=e.typeCondition,r=e.variableDefinitions,i=e.directives,a=e.selectionSet;return"fragment ".concat(t).concat(t8("(",t6(r,", "),")")," ")+"on ".concat(n," ").concat(t8("",t6(i," ")," "))+a},IntValue:function(e){return e.value},FloatValue:function(e){return e.value},StringValue:function(e,t){var n=e.value;return e.block?(0,t1.LZ)(n,"description"===t?"":" "):JSON.stringify(n)},BooleanValue:function(e){return e.value?"true":"false"},NullValue:function(){return"null"},EnumValue:function(e){return e.value},ListValue:function(e){return"["+t6(e.values,", ")+"]"},ObjectValue:function(e){return"{"+t6(e.fields,", ")+"}"},ObjectField:function(e){var t;return e.name+": "+e.value},Directive:function(e){var t;return"@"+e.name+t8("(",t6(e.arguments,", "),")")},NamedType:function(e){return e.name},ListType:function(e){return"["+e.type+"]"},NonNullType:function(e){return e.type+"!"},SchemaDefinition:t4(function(e){var t=e.directives,n=e.operationTypes;return t6(["schema",t6(t," "),t5(n)]," ")}),OperationTypeDefinition:function(e){var t;return e.operation+": "+e.type},ScalarTypeDefinition:t4(function(e){var t;return t6(["scalar",e.name,t6(e.directives," ")]," ")}),ObjectTypeDefinition:t4(function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t6(["type",t,t8("implements ",t6(n," & ")),t6(r," "),t5(i)]," ")}),FieldDefinition:t4(function(e){var t=e.name,n=e.arguments,r=e.type,i=e.directives;return t+(ne(n)?t8("(\n",t9(t6(n,"\n")),"\n)"):t8("(",t6(n,", "),")"))+": "+r+t8(" ",t6(i," "))}),InputValueDefinition:t4(function(e){var t=e.name,n=e.type,r=e.defaultValue,i=e.directives;return t6([t+": "+n,t8("= ",r),t6(i," ")]," ")}),InterfaceTypeDefinition:t4(function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t6(["interface",t,t8("implements ",t6(n," & ")),t6(r," "),t5(i)]," ")}),UnionTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.types;return t6(["union",t,t6(n," "),r&&0!==r.length?"= "+t6(r," | "):""]," ")}),EnumTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.values;return t6(["enum",t,t6(n," "),t5(r)]," ")}),EnumValueDefinition:t4(function(e){var t;return t6([e.name,t6(e.directives," ")]," ")}),InputObjectTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.fields;return t6(["input",t,t6(n," "),t5(r)]," ")}),DirectiveDefinition:t4(function(e){var t=e.name,n=e.arguments,r=e.repeatable,i=e.locations;return"directive @"+t+(ne(n)?t8("(\n",t9(t6(n,"\n")),"\n)"):t8("(",t6(n,", "),")"))+(r?" repeatable":"")+" on "+t6(i," | ")}),SchemaExtension:function(e){var t=e.directives,n=e.operationTypes;return t6(["extend schema",t6(t," "),t5(n)]," ")},ScalarTypeExtension:function(e){var t;return t6(["extend scalar",e.name,t6(e.directives," ")]," ")},ObjectTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t6(["extend type",t,t8("implements ",t6(n," & ")),t6(r," "),t5(i)]," ")},InterfaceTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t6(["extend interface",t,t8("implements ",t6(n," & ")),t6(r," "),t5(i)]," ")},UnionTypeExtension:function(e){var t=e.name,n=e.directives,r=e.types;return t6(["extend union",t,t6(n," "),r&&0!==r.length?"= "+t6(r," | "):""]," ")},EnumTypeExtension:function(e){var t=e.name,n=e.directives,r=e.values;return t6(["extend enum",t,t6(n," "),t5(r)]," ")},InputObjectTypeExtension:function(e){var t=e.name,n=e.directives,r=e.fields;return t6(["extend input",t,t6(n," "),t5(r)]," ")}};function t4(e){return function(t){return t6([t.description,e(t)],"\n")}}function t6(e){var t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return null!==(t=null==e?void 0:e.filter(function(e){return e}).join(n))&&void 0!==t?t:""}function t5(e){return t8("{\n",t9(t6(e,"\n")),"\n}")}function t8(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return null!=t&&""!==t?e+t+n:""}function t9(e){return t8(" ",e.replace(/\n/g,"\n "))}function t7(e){return -1!==e.indexOf("\n")}function ne(e){return null!=e&&e.some(t7)}var nt,nn,nr,ni={http:{includeQuery:!0,includeExtensions:!1,preserveHeaderCase:!1},headers:{accept:"*/*","content-type":"application/json"},options:{method:"POST"}},na=function(e,t){return t(e)};function no(e,t){for(var n=[],r=2;rObject.create(null),{forEach:nv,slice:ny}=Array.prototype,{hasOwnProperty:nw}=Object.prototype;class n_{constructor(e=!0,t=ng){this.weakness=e,this.makeData=t}lookup(...e){return this.lookupArray(e)}lookupArray(e){let t=this;return nv.call(e,e=>t=t.getChildTrie(e)),nw.call(t,"data")?t.data:t.data=this.makeData(ny.call(e))}peek(...e){return this.peekArray(e)}peekArray(e){let t=this;for(let n=0,r=e.length;t&&n=0;--o)t.definitions[o].kind===nL.h.OPERATION_DEFINITION&&++a;var s=nN(e),u=e.some(function(e){return e.remove}),c=function(e){return u&&e&&e.some(s)},l=new Map,f=!1,d={enter:function(e){if(c(e.directives))return f=!0,null}},h=tl(t,{Field:d,InlineFragment:d,VariableDefinition:{enter:function(){return!1}},Variable:{enter:function(e,t,n,r,a){var o=i(a);o&&o.variables.add(e.name.value)}},FragmentSpread:{enter:function(e,t,n,r,a){if(c(e.directives))return f=!0,null;var o=i(a);o&&o.fragmentSpreads.add(e.name.value)}},FragmentDefinition:{enter:function(e,t,n,r){l.set(JSON.stringify(r),e)},leave:function(e,t,n,i){return e===l.get(JSON.stringify(i))?e:a>0&&e.selectionSet.selections.every(function(e){return e.kind===nL.h.FIELD&&"__typename"===e.name.value})?(r(e.name.value).removed=!0,f=!0,null):void 0}},Directive:{leave:function(e){if(s(e))return f=!0,null}}});if(!f)return t;var p=function(e){return e.transitiveVars||(e.transitiveVars=new Set(e.variables),e.removed||e.fragmentSpreads.forEach(function(t){p(r(t)).transitiveVars.forEach(function(t){e.transitiveVars.add(t)})})),e},b=new Set;h.definitions.forEach(function(e){e.kind===nL.h.OPERATION_DEFINITION?p(n(e.name&&e.name.value)).fragmentSpreads.forEach(function(e){b.add(e)}):e.kind!==nL.h.FRAGMENT_DEFINITION||0!==a||r(e.name.value).removed||b.add(e.name.value)}),b.forEach(function(e){p(r(e)).fragmentSpreads.forEach(function(e){b.add(e)})});var m=function(e){return!!(!b.has(e)||r(e).removed)},g={enter:function(e){if(m(e.name.value))return null}};return nD(tl(h,{FragmentSpread:g,FragmentDefinition:g,OperationDefinition:{leave:function(e){if(e.variableDefinitions){var t=p(n(e.name&&e.name.value)).transitiveVars;if(t.size0},t.prototype.tearDownQuery=function(){this.isTornDown||(this.concast&&this.observer&&(this.concast.removeObserver(this.observer),delete this.concast,delete this.observer),this.stopPolling(),this.subscriptions.forEach(function(e){return e.unsubscribe()}),this.subscriptions.clear(),this.queryManager.stopQuery(this.queryId),this.observers.clear(),this.isTornDown=!0)},t}(eT);function n4(e){var t=e.options,n=t.fetchPolicy,r=t.nextFetchPolicy;return"cache-and-network"===n||"network-only"===n?e.reobserve({fetchPolicy:"cache-first",nextFetchPolicy:function(){return(this.nextFetchPolicy=r,"function"==typeof r)?r.apply(this,arguments):n}}):e.reobserve()}function n6(e){__DEV__&&Q.kG.error("Unhandled error",e.message,e.stack)}function n5(e){__DEV__&&e&&__DEV__&&Q.kG.debug("Missing cache result fields: ".concat(JSON.stringify(e)),e)}function n8(e){return"network-only"===e||"no-cache"===e||"standby"===e}nK(n3);function n9(e){return e.kind===nL.h.FIELD||e.kind===nL.h.FRAGMENT_SPREAD||e.kind===nL.h.INLINE_FRAGMENT}function n7(e){return e.kind===Kind.SCALAR_TYPE_DEFINITION||e.kind===Kind.OBJECT_TYPE_DEFINITION||e.kind===Kind.INTERFACE_TYPE_DEFINITION||e.kind===Kind.UNION_TYPE_DEFINITION||e.kind===Kind.ENUM_TYPE_DEFINITION||e.kind===Kind.INPUT_OBJECT_TYPE_DEFINITION}function re(e){return e.kind===Kind.SCALAR_TYPE_EXTENSION||e.kind===Kind.OBJECT_TYPE_EXTENSION||e.kind===Kind.INTERFACE_TYPE_EXTENSION||e.kind===Kind.UNION_TYPE_EXTENSION||e.kind===Kind.ENUM_TYPE_EXTENSION||e.kind===Kind.INPUT_OBJECT_TYPE_EXTENSION}var rt=function(){return Object.create(null)},rn=Array.prototype,rr=rn.forEach,ri=rn.slice,ra=function(){function e(e,t){void 0===e&&(e=!0),void 0===t&&(t=rt),this.weakness=e,this.makeData=t}return e.prototype.lookup=function(){for(var e=[],t=0;tclass{constructor(){this.id=["slot",rc++,Date.now(),Math.random().toString(36).slice(2),].join(":")}hasValue(){for(let e=rs;e;e=e.parent)if(this.id in e.slots){let t=e.slots[this.id];if(t===ru)break;return e!==rs&&(rs.slots[this.id]=t),!0}return rs&&(rs.slots[this.id]=ru),!1}getValue(){if(this.hasValue())return rs.slots[this.id]}withValue(e,t,n,r){let i={__proto__:null,[this.id]:e},a=rs;rs={parent:a,slots:i};try{return t.apply(r,n)}finally{rs=a}}static bind(e){let t=rs;return function(){let n=rs;try{return rs=t,e.apply(this,arguments)}finally{rs=n}}}static noContext(e,t,n){if(!rs)return e.apply(n,t);{let r=rs;try{return rs=null,e.apply(n,t)}finally{rs=r}}}};function rf(e){try{return e()}catch(t){}}let rd="@wry/context:Slot",rh=rf(()=>globalThis)||rf(()=>global)||Object.create(null),rp=rh,rb=rp[rd]||Array[rd]||function(e){try{Object.defineProperty(rp,rd,{value:e,enumerable:!1,writable:!1,configurable:!0})}finally{return e}}(rl()),{bind:rm,noContext:rg}=rb;function rv(){}var ry=function(){function e(e,t){void 0===e&&(e=1/0),void 0===t&&(t=rv),this.max=e,this.dispose=t,this.map=new Map,this.newest=null,this.oldest=null}return e.prototype.has=function(e){return this.map.has(e)},e.prototype.get=function(e){var t=this.getNode(e);return t&&t.value},e.prototype.getNode=function(e){var t=this.map.get(e);if(t&&t!==this.newest){var n=t.older,r=t.newer;r&&(r.older=n),n&&(n.newer=r),t.older=this.newest,t.older.newer=t,t.newer=null,this.newest=t,t===this.oldest&&(this.oldest=r)}return t},e.prototype.set=function(e,t){var n=this.getNode(e);return n?n.value=t:(n={key:e,value:t,newer:null,older:this.newest},this.newest&&(this.newest.newer=n),this.newest=n,this.oldest=this.oldest||n,this.map.set(e,n),n.value)},e.prototype.clean=function(){for(;this.oldest&&this.map.size>this.max;)this.delete(this.oldest.key)},e.prototype.delete=function(e){var t=this.map.get(e);return!!t&&(t===this.newest&&(this.newest=t.older),t===this.oldest&&(this.oldest=t.newer),t.newer&&(t.newer.older=t.older),t.older&&(t.older.newer=t.newer),this.map.delete(e),this.dispose(t.value,e),!0)},e}(),rw=new rb,r_=Object.prototype.hasOwnProperty,rE=void 0===(n=Array.from)?function(e){var t=[];return e.forEach(function(e){return t.push(e)}),t}:n;function rS(e){var t=e.unsubscribe;"function"==typeof t&&(e.unsubscribe=void 0,t())}var rk=[],rx=100;function rT(e,t){if(!e)throw Error(t||"assertion failure")}function rM(e,t){var n=e.length;return n>0&&n===t.length&&e[n-1]===t[n-1]}function rO(e){switch(e.length){case 0:throw Error("unknown value");case 1:return e[0];case 2:throw e[1]}}function rA(e){return e.slice(0)}var rL=function(){function e(t){this.fn=t,this.parents=new Set,this.childValues=new Map,this.dirtyChildren=null,this.dirty=!0,this.recomputing=!1,this.value=[],this.deps=null,++e.count}return e.prototype.peek=function(){if(1===this.value.length&&!rN(this))return rC(this),this.value[0]},e.prototype.recompute=function(e){return rT(!this.recomputing,"already recomputing"),rC(this),rN(this)?rI(this,e):rO(this.value)},e.prototype.setDirty=function(){this.dirty||(this.dirty=!0,this.value.length=0,rR(this),rS(this))},e.prototype.dispose=function(){var e=this;this.setDirty(),rH(this),rF(this,function(t,n){t.setDirty(),r$(t,e)})},e.prototype.forget=function(){this.dispose()},e.prototype.dependOn=function(e){e.add(this),this.deps||(this.deps=rk.pop()||new Set),this.deps.add(e)},e.prototype.forgetDeps=function(){var e=this;this.deps&&(rE(this.deps).forEach(function(t){return t.delete(e)}),this.deps.clear(),rk.push(this.deps),this.deps=null)},e.count=0,e}();function rC(e){var t=rw.getValue();if(t)return e.parents.add(t),t.childValues.has(e)||t.childValues.set(e,[]),rN(e)?rY(t,e):rB(t,e),t}function rI(e,t){return rH(e),rw.withValue(e,rD,[e,t]),rz(e,t)&&rP(e),rO(e.value)}function rD(e,t){e.recomputing=!0,e.value.length=0;try{e.value[0]=e.fn.apply(null,t)}catch(n){e.value[1]=n}e.recomputing=!1}function rN(e){return e.dirty||!!(e.dirtyChildren&&e.dirtyChildren.size)}function rP(e){e.dirty=!1,!rN(e)&&rj(e)}function rR(e){rF(e,rY)}function rj(e){rF(e,rB)}function rF(e,t){var n=e.parents.size;if(n)for(var r=rE(e.parents),i=0;i0&&e.childValues.forEach(function(t,n){r$(e,n)}),e.forgetDeps(),rT(null===e.dirtyChildren)}function r$(e,t){t.parents.delete(e),e.childValues.delete(t),rU(e,t)}function rz(e,t){if("function"==typeof e.subscribe)try{rS(e),e.unsubscribe=e.subscribe.apply(null,t)}catch(n){return e.setDirty(),!1}return!0}var rG={setDirty:!0,dispose:!0,forget:!0};function rW(e){var t=new Map,n=e&&e.subscribe;function r(e){var r=rw.getValue();if(r){var i=t.get(e);i||t.set(e,i=new Set),r.dependOn(i),"function"==typeof n&&(rS(i),i.unsubscribe=n(e))}}return r.dirty=function(e,n){var r=t.get(e);if(r){var i=n&&r_.call(rG,n)?n:"setDirty";rE(r).forEach(function(e){return e[i]()}),t.delete(e),rS(r)}},r}function rK(){var e=new ra("function"==typeof WeakMap);return function(){return e.lookupArray(arguments)}}var rV=rK(),rq=new Set;function rZ(e,t){void 0===t&&(t=Object.create(null));var n=new ry(t.max||65536,function(e){return e.dispose()}),r=t.keyArgs,i=t.makeCacheKey||rK(),a=function(){var a=i.apply(null,r?r.apply(null,arguments):arguments);if(void 0===a)return e.apply(null,arguments);var o=n.get(a);o||(n.set(a,o=new rL(e)),o.subscribe=t.subscribe,o.forget=function(){return n.delete(a)});var s=o.recompute(Array.prototype.slice.call(arguments));return n.set(a,o),rq.add(n),rw.hasValue()||(rq.forEach(function(e){return e.clean()}),rq.clear()),s};function o(e){var t=n.get(e);t&&t.setDirty()}function s(e){var t=n.get(e);if(t)return t.peek()}function u(e){return n.delete(e)}return Object.defineProperty(a,"size",{get:function(){return n.map.size},configurable:!1,enumerable:!1}),a.dirtyKey=o,a.dirty=function(){o(i.apply(null,arguments))},a.peekKey=s,a.peek=function(){return s(i.apply(null,arguments))},a.forgetKey=u,a.forget=function(){return u(i.apply(null,arguments))},a.makeCacheKey=i,a.getKey=r?function(){return i.apply(null,r.apply(null,arguments))}:i,Object.freeze(a)}var rX=new rb,rJ=new WeakMap;function rQ(e){var t=rJ.get(e);return t||rJ.set(e,t={vars:new Set,dep:rW()}),t}function r1(e){rQ(e).vars.forEach(function(t){return t.forgetCache(e)})}function r0(e){rQ(e).vars.forEach(function(t){return t.attachCache(e)})}function r2(e){var t=new Set,n=new Set,r=function(a){if(arguments.length>0){if(e!==a){e=a,t.forEach(function(e){rQ(e).dep.dirty(r),r3(e)});var o=Array.from(n);n.clear(),o.forEach(function(t){return t(e)})}}else{var s=rX.getValue();s&&(i(s),rQ(s).dep(r))}return e};r.onNextChange=function(e){return n.add(e),function(){n.delete(e)}};var i=r.attachCache=function(e){return t.add(e),rQ(e).vars.add(r),r};return r.forgetCache=function(e){return t.delete(e)},r}function r3(e){e.broadcastWatches&&e.broadcastWatches()}var r4=function(){function e(e){var t=e.cache,n=e.client,r=e.resolvers,i=e.fragmentMatcher;this.selectionsToResolveCache=new WeakMap,this.cache=t,n&&(this.client=n),r&&this.addResolvers(r),i&&this.setFragmentMatcher(i)}return e.prototype.addResolvers=function(e){var t=this;this.resolvers=this.resolvers||{},Array.isArray(e)?e.forEach(function(e){t.resolvers=tj(t.resolvers,e)}):this.resolvers=tj(this.resolvers,e)},e.prototype.setResolvers=function(e){this.resolvers={},this.addResolvers(e)},e.prototype.getResolvers=function(){return this.resolvers||{}},e.prototype.runResolvers=function(e){var t=e.document,n=e.remoteResult,r=e.context,i=e.variables,a=e.onlyRunForcedResolvers,o=void 0!==a&&a;return(0,en.mG)(this,void 0,void 0,function(){return(0,en.Jh)(this,function(e){return t?[2,this.resolveDocument(t,n.data,r,i,this.fragmentMatcher,o).then(function(e){return(0,en.pi)((0,en.pi)({},n),{data:e.result})})]:[2,n]})})},e.prototype.setFragmentMatcher=function(e){this.fragmentMatcher=e},e.prototype.getFragmentMatcher=function(){return this.fragmentMatcher},e.prototype.clientQuery=function(e){return tb(["client"],e)&&this.resolvers?e:null},e.prototype.serverQuery=function(e){return n$(e)},e.prototype.prepareContext=function(e){var t=this.cache;return(0,en.pi)((0,en.pi)({},e),{cache:t,getCacheKey:function(e){return t.identify(e)}})},e.prototype.addExportedVariables=function(e,t,n){return void 0===t&&(t={}),void 0===n&&(n={}),(0,en.mG)(this,void 0,void 0,function(){return(0,en.Jh)(this,function(r){return e?[2,this.resolveDocument(e,this.buildRootValueFromCache(e,t)||{},this.prepareContext(n),t).then(function(e){return(0,en.pi)((0,en.pi)({},t),e.exportedVariables)})]:[2,(0,en.pi)({},t)]})})},e.prototype.shouldForceResolvers=function(e){var t=!1;return tl(e,{Directive:{enter:function(e){if("client"===e.name.value&&e.arguments&&(t=e.arguments.some(function(e){return"always"===e.name.value&&"BooleanValue"===e.value.kind&&!0===e.value.value})))return tc}}}),t},e.prototype.buildRootValueFromCache=function(e,t){return this.cache.diff({query:nH(e),variables:t,returnPartialData:!0,optimistic:!1}).result},e.prototype.resolveDocument=function(e,t,n,r,i,a){return void 0===n&&(n={}),void 0===r&&(r={}),void 0===i&&(i=function(){return!0}),void 0===a&&(a=!1),(0,en.mG)(this,void 0,void 0,function(){var o,s,u,c,l,f,d,h,p,b,m;return(0,en.Jh)(this,function(g){return o=e8(e),s=e4(e),u=eL(s),c=this.collectSelectionsToResolve(o,u),f=(l=o.operation)?l.charAt(0).toUpperCase()+l.slice(1):"Query",d=this,h=d.cache,p=d.client,b={fragmentMap:u,context:(0,en.pi)((0,en.pi)({},n),{cache:h,client:p}),variables:r,fragmentMatcher:i,defaultOperationType:f,exportedVariables:{},selectionsToResolve:c,onlyRunForcedResolvers:a},m=!1,[2,this.resolveSelectionSet(o.selectionSet,m,t,b).then(function(e){return{result:e,exportedVariables:b.exportedVariables}})]})})},e.prototype.resolveSelectionSet=function(e,t,n,r){return(0,en.mG)(this,void 0,void 0,function(){var i,a,o,s,u,c=this;return(0,en.Jh)(this,function(l){return i=r.fragmentMap,a=r.context,o=r.variables,s=[n],u=function(e){return(0,en.mG)(c,void 0,void 0,function(){var u,c;return(0,en.Jh)(this,function(l){return(t||r.selectionsToResolve.has(e))&&td(e,o)?eQ(e)?[2,this.resolveField(e,t,n,r).then(function(t){var n;void 0!==t&&s.push(((n={})[eX(e)]=t,n))})]:(e1(e)?u=e:(u=i[e.name.value],__DEV__?(0,Q.kG)(u,"No fragment named ".concat(e.name.value)):(0,Q.kG)(u,11)),u&&u.typeCondition&&(c=u.typeCondition.name.value,r.fragmentMatcher(n,c,a)))?[2,this.resolveSelectionSet(u.selectionSet,t,n,r).then(function(e){s.push(e)})]:[2]:[2]})})},[2,Promise.all(e.selections.map(u)).then(function(){return tF(s)})]})})},e.prototype.resolveField=function(e,t,n,r){return(0,en.mG)(this,void 0,void 0,function(){var i,a,o,s,u,c,l,f,d,h=this;return(0,en.Jh)(this,function(p){return n?(i=r.variables,a=e.name.value,o=eX(e),s=a!==o,c=Promise.resolve(u=n[o]||n[a]),(!r.onlyRunForcedResolvers||this.shouldForceResolvers(e))&&(l=n.__typename||r.defaultOperationType,(f=this.resolvers&&this.resolvers[l])&&(d=f[s?a:o])&&(c=Promise.resolve(rX.withValue(this.cache,d,[n,eZ(e,i),r.context,{field:e,fragmentMap:r.fragmentMap},])))),[2,c.then(function(n){if(void 0===n&&(n=u),e.directives&&e.directives.forEach(function(e){"export"===e.name.value&&e.arguments&&e.arguments.forEach(function(e){"as"===e.name.value&&"StringValue"===e.value.kind&&(r.exportedVariables[e.value.value]=n)})}),!e.selectionSet||null==n)return n;var i,a,o=null!==(a=null===(i=e.directives)||void 0===i?void 0:i.some(function(e){return"client"===e.name.value}))&&void 0!==a&&a;return Array.isArray(n)?h.resolveSubSelectedArray(e,t||o,n,r):e.selectionSet?h.resolveSelectionSet(e.selectionSet,t||o,n,r):void 0})]):[2,null]})})},e.prototype.resolveSubSelectedArray=function(e,t,n,r){var i=this;return Promise.all(n.map(function(n){return null===n?null:Array.isArray(n)?i.resolveSubSelectedArray(e,t,n,r):e.selectionSet?i.resolveSelectionSet(e.selectionSet,t,n,r):void 0}))},e.prototype.collectSelectionsToResolve=function(e,t){var n=function(e){return!Array.isArray(e)},r=this.selectionsToResolveCache;function i(e){if(!r.has(e)){var a=new Set;r.set(e,a),tl(e,{Directive:function(e,t,r,i,o){"client"===e.name.value&&o.forEach(function(e){n(e)&&n9(e)&&a.add(e)})},FragmentSpread:function(e,r,o,s,u){var c=t[e.name.value];__DEV__?(0,Q.kG)(c,"No fragment named ".concat(e.name.value)):(0,Q.kG)(c,12);var l=i(c);l.size>0&&(u.forEach(function(e){n(e)&&n9(e)&&a.add(e)}),a.add(e),l.forEach(function(e){a.add(e)}))}})}return r.get(e)}return i(e)},e}(),r6=new(t_.mr?WeakMap:Map);function r5(e,t){var n=e[t];"function"==typeof n&&(e[t]=function(){return r6.set(e,(r6.get(e)+1)%1e15),n.apply(this,arguments)})}function r8(e){e.notifyTimeout&&(clearTimeout(e.notifyTimeout),e.notifyTimeout=void 0)}var r9=function(){function e(e,t){void 0===t&&(t=e.generateQueryId()),this.queryId=t,this.listeners=new Set,this.document=null,this.lastRequestId=1,this.subscriptions=new Set,this.stopped=!1,this.dirty=!1,this.observableQuery=null;var n=this.cache=e.cache;r6.has(n)||(r6.set(n,0),r5(n,"evict"),r5(n,"modify"),r5(n,"reset"))}return e.prototype.init=function(e){var t=e.networkStatus||nZ.I.loading;return this.variables&&this.networkStatus!==nZ.I.loading&&!(0,nm.D)(this.variables,e.variables)&&(t=nZ.I.setVariables),(0,nm.D)(e.variables,this.variables)||(this.lastDiff=void 0),Object.assign(this,{document:e.document,variables:e.variables,networkError:null,graphQLErrors:this.graphQLErrors||[],networkStatus:t}),e.observableQuery&&this.setObservableQuery(e.observableQuery),e.lastRequestId&&(this.lastRequestId=e.lastRequestId),this},e.prototype.reset=function(){r8(this),this.dirty=!1},e.prototype.getDiff=function(e){void 0===e&&(e=this.variables);var t=this.getDiffOptions(e);if(this.lastDiff&&(0,nm.D)(t,this.lastDiff.options))return this.lastDiff.diff;this.updateWatch(this.variables=e);var n=this.observableQuery;if(n&&"no-cache"===n.options.fetchPolicy)return{complete:!1};var r=this.cache.diff(t);return this.updateLastDiff(r,t),r},e.prototype.updateLastDiff=function(e,t){this.lastDiff=e?{diff:e,options:t||this.getDiffOptions()}:void 0},e.prototype.getDiffOptions=function(e){var t;return void 0===e&&(e=this.variables),{query:this.document,variables:e,returnPartialData:!0,optimistic:!0,canonizeResults:null===(t=this.observableQuery)||void 0===t?void 0:t.options.canonizeResults}},e.prototype.setDiff=function(e){var t=this,n=this.lastDiff&&this.lastDiff.diff;this.updateLastDiff(e),this.dirty||(0,nm.D)(n&&n.result,e&&e.result)||(this.dirty=!0,this.notifyTimeout||(this.notifyTimeout=setTimeout(function(){return t.notify()},0)))},e.prototype.setObservableQuery=function(e){var t=this;e!==this.observableQuery&&(this.oqListener&&this.listeners.delete(this.oqListener),this.observableQuery=e,e?(e.queryInfo=this,this.listeners.add(this.oqListener=function(){t.getDiff().fromOptimisticTransaction?e.observe():n4(e)})):delete this.oqListener)},e.prototype.notify=function(){var e=this;r8(this),this.shouldNotify()&&this.listeners.forEach(function(t){return t(e)}),this.dirty=!1},e.prototype.shouldNotify=function(){if(!this.dirty||!this.listeners.size)return!1;if((0,nZ.O)(this.networkStatus)&&this.observableQuery){var e=this.observableQuery.options.fetchPolicy;if("cache-only"!==e&&"cache-and-network"!==e)return!1}return!0},e.prototype.stop=function(){if(!this.stopped){this.stopped=!0,this.reset(),this.cancel(),this.cancel=e.prototype.cancel,this.subscriptions.forEach(function(e){return e.unsubscribe()});var t=this.observableQuery;t&&t.stopPolling()}},e.prototype.cancel=function(){},e.prototype.updateWatch=function(e){var t=this;void 0===e&&(e=this.variables);var n=this.observableQuery;if(!n||"no-cache"!==n.options.fetchPolicy){var r=(0,en.pi)((0,en.pi)({},this.getDiffOptions(e)),{watcher:this,callback:function(e){return t.setDiff(e)}});this.lastWatch&&(0,nm.D)(r,this.lastWatch)||(this.cancel(),this.cancel=this.cache.watch(this.lastWatch=r))}},e.prototype.resetLastWrite=function(){this.lastWrite=void 0},e.prototype.shouldWrite=function(e,t){var n=this.lastWrite;return!(n&&n.dmCount===r6.get(this.cache)&&(0,nm.D)(t,n.variables)&&(0,nm.D)(e.data,n.result.data))},e.prototype.markResult=function(e,t,n,r){var i=this,a=new tB,o=(0,tP.O)(e.errors)?e.errors.slice(0):[];if(this.reset(),"incremental"in e&&(0,tP.O)(e.incremental)){var s=tG(this.getDiff().result,e);e.data=s}else if("hasNext"in e&&e.hasNext){var u=this.getDiff();e.data=a.merge(u.result,e.data)}this.graphQLErrors=o,"no-cache"===n.fetchPolicy?this.updateLastDiff({result:e.data,complete:!0},this.getDiffOptions(n.variables)):0!==r&&(r7(e,n.errorPolicy)?this.cache.performTransaction(function(a){if(i.shouldWrite(e,n.variables))a.writeQuery({query:t,data:e.data,variables:n.variables,overwrite:1===r}),i.lastWrite={result:e,variables:n.variables,dmCount:r6.get(i.cache)};else if(i.lastDiff&&i.lastDiff.diff.complete){e.data=i.lastDiff.diff.result;return}var o=i.getDiffOptions(n.variables),s=a.diff(o);i.stopped||i.updateWatch(n.variables),i.updateLastDiff(s,o),s.complete&&(e.data=s.result)}):this.lastWrite=void 0)},e.prototype.markReady=function(){return this.networkError=null,this.networkStatus=nZ.I.ready},e.prototype.markError=function(e){return this.networkStatus=nZ.I.error,this.lastWrite=void 0,this.reset(),e.graphQLErrors&&(this.graphQLErrors=e.graphQLErrors),e.networkError&&(this.networkError=e.networkError),e},e}();function r7(e,t){void 0===t&&(t="none");var n="ignore"===t||"all"===t,r=!nO(e);return!r&&n&&e.data&&(r=!0),r}var ie=Object.prototype.hasOwnProperty,it=function(){function e(e){var t=e.cache,n=e.link,r=e.defaultOptions,i=e.queryDeduplication,a=void 0!==i&&i,o=e.onBroadcast,s=e.ssrMode,u=void 0!==s&&s,c=e.clientAwareness,l=void 0===c?{}:c,f=e.localState,d=e.assumeImmutableResults;this.clientAwareness={},this.queries=new Map,this.fetchCancelFns=new Map,this.transformCache=new(t_.mr?WeakMap:Map),this.queryIdCounter=1,this.requestIdCounter=1,this.mutationIdCounter=1,this.inFlightLinkObservables=new Map,this.cache=t,this.link=n,this.defaultOptions=r||Object.create(null),this.queryDeduplication=a,this.clientAwareness=l,this.localState=f||new r4({cache:t}),this.ssrMode=u,this.assumeImmutableResults=!!d,(this.onBroadcast=o)&&(this.mutationStore=Object.create(null))}return e.prototype.stop=function(){var e=this;this.queries.forEach(function(t,n){e.stopQueryNoBroadcast(n)}),this.cancelPendingFetches(__DEV__?new Q.ej("QueryManager stopped while query was in flight"):new Q.ej(14))},e.prototype.cancelPendingFetches=function(e){this.fetchCancelFns.forEach(function(t){return t(e)}),this.fetchCancelFns.clear()},e.prototype.mutate=function(e){var t,n,r=e.mutation,i=e.variables,a=e.optimisticResponse,o=e.updateQueries,s=e.refetchQueries,u=void 0===s?[]:s,c=e.awaitRefetchQueries,l=void 0!==c&&c,f=e.update,d=e.onQueryUpdated,h=e.fetchPolicy,p=void 0===h?(null===(t=this.defaultOptions.mutate)||void 0===t?void 0:t.fetchPolicy)||"network-only":h,b=e.errorPolicy,m=void 0===b?(null===(n=this.defaultOptions.mutate)||void 0===n?void 0:n.errorPolicy)||"none":b,g=e.keepRootFields,v=e.context;return(0,en.mG)(this,void 0,void 0,function(){var e,t,n,s,c,h;return(0,en.Jh)(this,function(b){switch(b.label){case 0:if(__DEV__?(0,Q.kG)(r,"mutation option is required. You must specify your GraphQL document in the mutation option."):(0,Q.kG)(r,15),__DEV__?(0,Q.kG)("network-only"===p||"no-cache"===p,"Mutations support only 'network-only' or 'no-cache' fetchPolicy strings. The default `network-only` behavior automatically writes mutation results to the cache. Passing `no-cache` skips the cache write."):(0,Q.kG)("network-only"===p||"no-cache"===p,16),e=this.generateMutationId(),n=(t=this.transform(r)).document,s=t.hasClientExports,r=this.cache.transformForLink(n),i=this.getVariables(r,i),!s)return[3,2];return[4,this.localState.addExportedVariables(r,i,v)];case 1:i=b.sent(),b.label=2;case 2:return c=this.mutationStore&&(this.mutationStore[e]={mutation:r,variables:i,loading:!0,error:null}),a&&this.markMutationOptimistic(a,{mutationId:e,document:r,variables:i,fetchPolicy:p,errorPolicy:m,context:v,updateQueries:o,update:f,keepRootFields:g}),this.broadcastQueries(),h=this,[2,new Promise(function(t,n){return nM(h.getObservableFromLink(r,(0,en.pi)((0,en.pi)({},v),{optimisticResponse:a}),i,!1),function(t){if(nO(t)&&"none"===m)throw new tN.cA({graphQLErrors:nA(t)});c&&(c.loading=!1,c.error=null);var n=(0,en.pi)({},t);return"function"==typeof u&&(u=u(n)),"ignore"===m&&nO(n)&&delete n.errors,h.markMutationResult({mutationId:e,result:n,document:r,variables:i,fetchPolicy:p,errorPolicy:m,context:v,update:f,updateQueries:o,awaitRefetchQueries:l,refetchQueries:u,removeOptimistic:a?e:void 0,onQueryUpdated:d,keepRootFields:g})}).subscribe({next:function(e){h.broadcastQueries(),"hasNext"in e&&!1!==e.hasNext||t(e)},error:function(t){c&&(c.loading=!1,c.error=t),a&&h.cache.removeOptimistic(e),h.broadcastQueries(),n(t instanceof tN.cA?t:new tN.cA({networkError:t}))}})})]}})})},e.prototype.markMutationResult=function(e,t){var n=this;void 0===t&&(t=this.cache);var r=e.result,i=[],a="no-cache"===e.fetchPolicy;if(!a&&r7(r,e.errorPolicy)){if(tU(r)||i.push({result:r.data,dataId:"ROOT_MUTATION",query:e.document,variables:e.variables}),tU(r)&&(0,tP.O)(r.incremental)){var o=t.diff({id:"ROOT_MUTATION",query:this.transform(e.document).asQuery,variables:e.variables,optimistic:!1,returnPartialData:!0}),s=void 0;o.result&&(s=tG(o.result,r)),void 0!==s&&(r.data=s,i.push({result:s,dataId:"ROOT_MUTATION",query:e.document,variables:e.variables}))}var u=e.updateQueries;u&&this.queries.forEach(function(e,a){var o=e.observableQuery,s=o&&o.queryName;if(s&&ie.call(u,s)){var c,l=u[s],f=n.queries.get(a),d=f.document,h=f.variables,p=t.diff({query:d,variables:h,returnPartialData:!0,optimistic:!1}),b=p.result;if(p.complete&&b){var m=l(b,{mutationResult:r,queryName:d&&e3(d)||void 0,queryVariables:h});m&&i.push({result:m,dataId:"ROOT_QUERY",query:d,variables:h})}}})}if(i.length>0||e.refetchQueries||e.update||e.onQueryUpdated||e.removeOptimistic){var c=[];if(this.refetchQueries({updateCache:function(t){a||i.forEach(function(e){return t.write(e)});var o=e.update,s=!t$(r)||tU(r)&&!r.hasNext;if(o){if(!a){var u=t.diff({id:"ROOT_MUTATION",query:n.transform(e.document).asQuery,variables:e.variables,optimistic:!1,returnPartialData:!0});u.complete&&("incremental"in(r=(0,en.pi)((0,en.pi)({},r),{data:u.result}))&&delete r.incremental,"hasNext"in r&&delete r.hasNext)}s&&o(t,r,{context:e.context,variables:e.variables})}a||e.keepRootFields||!s||t.modify({id:"ROOT_MUTATION",fields:function(e,t){var n=t.fieldName,r=t.DELETE;return"__typename"===n?e:r}})},include:e.refetchQueries,optimistic:!1,removeOptimistic:e.removeOptimistic,onQueryUpdated:e.onQueryUpdated||null}).forEach(function(e){return c.push(e)}),e.awaitRefetchQueries||e.onQueryUpdated)return Promise.all(c).then(function(){return r})}return Promise.resolve(r)},e.prototype.markMutationOptimistic=function(e,t){var n=this,r="function"==typeof e?e(t.variables):e;return this.cache.recordOptimisticTransaction(function(e){try{n.markMutationResult((0,en.pi)((0,en.pi)({},t),{result:{data:r}}),e)}catch(i){__DEV__&&Q.kG.error(i)}},t.mutationId)},e.prototype.fetchQuery=function(e,t,n){return this.fetchQueryObservable(e,t,n).promise},e.prototype.getQueryStore=function(){var e=Object.create(null);return this.queries.forEach(function(t,n){e[n]={variables:t.variables,networkStatus:t.networkStatus,networkError:t.networkError,graphQLErrors:t.graphQLErrors}}),e},e.prototype.resetErrors=function(e){var t=this.queries.get(e);t&&(t.networkError=void 0,t.graphQLErrors=[])},e.prototype.transform=function(e){var t=this.transformCache;if(!t.has(e)){var n=this.cache.transformDocument(e),r=nY(n),i=this.localState.clientQuery(n),a=r&&this.localState.serverQuery(r),o={document:n,hasClientExports:tm(n),hasForcedResolvers:this.localState.shouldForceResolvers(n),clientQuery:i,serverQuery:a,defaultVars:e9(e2(n)),asQuery:(0,en.pi)((0,en.pi)({},n),{definitions:n.definitions.map(function(e){return"OperationDefinition"===e.kind&&"query"!==e.operation?(0,en.pi)((0,en.pi)({},e),{operation:"query"}):e})})},s=function(e){e&&!t.has(e)&&t.set(e,o)};s(e),s(n),s(i),s(a)}return t.get(e)},e.prototype.getVariables=function(e,t){return(0,en.pi)((0,en.pi)({},this.transform(e).defaultVars),t)},e.prototype.watchQuery=function(e){void 0===(e=(0,en.pi)((0,en.pi)({},e),{variables:this.getVariables(e.query,e.variables)})).notifyOnNetworkStatusChange&&(e.notifyOnNetworkStatusChange=!1);var t=new r9(this),n=new n3({queryManager:this,queryInfo:t,options:e});return this.queries.set(n.queryId,t),t.init({document:n.query,observableQuery:n,variables:n.variables}),n},e.prototype.query=function(e,t){var n=this;return void 0===t&&(t=this.generateQueryId()),__DEV__?(0,Q.kG)(e.query,"query option is required. You must specify your GraphQL document in the query option."):(0,Q.kG)(e.query,17),__DEV__?(0,Q.kG)("Document"===e.query.kind,'You must wrap the query string in a "gql" tag.'):(0,Q.kG)("Document"===e.query.kind,18),__DEV__?(0,Q.kG)(!e.returnPartialData,"returnPartialData option only supported on watchQuery."):(0,Q.kG)(!e.returnPartialData,19),__DEV__?(0,Q.kG)(!e.pollInterval,"pollInterval option only supported on watchQuery."):(0,Q.kG)(!e.pollInterval,20),this.fetchQuery(t,e).finally(function(){return n.stopQuery(t)})},e.prototype.generateQueryId=function(){return String(this.queryIdCounter++)},e.prototype.generateRequestId=function(){return this.requestIdCounter++},e.prototype.generateMutationId=function(){return String(this.mutationIdCounter++)},e.prototype.stopQueryInStore=function(e){this.stopQueryInStoreNoBroadcast(e),this.broadcastQueries()},e.prototype.stopQueryInStoreNoBroadcast=function(e){var t=this.queries.get(e);t&&t.stop()},e.prototype.clearStore=function(e){return void 0===e&&(e={discardWatches:!0}),this.cancelPendingFetches(__DEV__?new Q.ej("Store reset while query was in flight (not completed in link chain)"):new Q.ej(21)),this.queries.forEach(function(e){e.observableQuery?e.networkStatus=nZ.I.loading:e.stop()}),this.mutationStore&&(this.mutationStore=Object.create(null)),this.cache.reset(e)},e.prototype.getObservableQueries=function(e){var t=this;void 0===e&&(e="active");var n=new Map,r=new Map,i=new Set;return Array.isArray(e)&&e.forEach(function(e){"string"==typeof e?r.set(e,!1):eN(e)?r.set(t.transform(e).document,!1):(0,eO.s)(e)&&e.query&&i.add(e)}),this.queries.forEach(function(t,i){var a=t.observableQuery,o=t.document;if(a){if("all"===e){n.set(i,a);return}var s=a.queryName;if("standby"===a.options.fetchPolicy||"active"===e&&!a.hasObservers())return;("active"===e||s&&r.has(s)||o&&r.has(o))&&(n.set(i,a),s&&r.set(s,!0),o&&r.set(o,!0))}}),i.size&&i.forEach(function(e){var r=nG("legacyOneTimeQuery"),i=t.getQuery(r).init({document:e.query,variables:e.variables}),a=new n3({queryManager:t,queryInfo:i,options:(0,en.pi)((0,en.pi)({},e),{fetchPolicy:"network-only"})});(0,Q.kG)(a.queryId===r),i.setObservableQuery(a),n.set(r,a)}),__DEV__&&r.size&&r.forEach(function(e,t){!e&&__DEV__&&Q.kG.warn("Unknown query ".concat("string"==typeof t?"named ":"").concat(JSON.stringify(t,null,2)," requested in refetchQueries options.include array"))}),n},e.prototype.reFetchObservableQueries=function(e){var t=this;void 0===e&&(e=!1);var n=[];return this.getObservableQueries(e?"all":"active").forEach(function(r,i){var a=r.options.fetchPolicy;r.resetLastResults(),(e||"standby"!==a&&"cache-only"!==a)&&n.push(r.refetch()),t.getQuery(i).setDiff(null)}),this.broadcastQueries(),Promise.all(n)},e.prototype.setObservableQuery=function(e){this.getQuery(e.queryId).setObservableQuery(e)},e.prototype.startGraphQLSubscription=function(e){var t=this,n=e.query,r=e.fetchPolicy,i=e.errorPolicy,a=e.variables,o=e.context,s=void 0===o?{}:o;n=this.transform(n).document,a=this.getVariables(n,a);var u=function(e){return t.getObservableFromLink(n,s,e).map(function(a){"no-cache"!==r&&(r7(a,i)&&t.cache.write({query:n,result:a.data,dataId:"ROOT_SUBSCRIPTION",variables:e}),t.broadcastQueries());var o=nO(a),s=(0,tN.ls)(a);if(o||s){var u={};throw o&&(u.graphQLErrors=a.errors),s&&(u.protocolErrors=a.extensions[tN.YG]),new tN.cA(u)}return a})};if(this.transform(n).hasClientExports){var c=this.localState.addExportedVariables(n,a,s).then(u);return new eT(function(e){var t=null;return c.then(function(n){return t=n.subscribe(e)},e.error),function(){return t&&t.unsubscribe()}})}return u(a)},e.prototype.stopQuery=function(e){this.stopQueryNoBroadcast(e),this.broadcastQueries()},e.prototype.stopQueryNoBroadcast=function(e){this.stopQueryInStoreNoBroadcast(e),this.removeQuery(e)},e.prototype.removeQuery=function(e){this.fetchCancelFns.delete(e),this.queries.has(e)&&(this.getQuery(e).stop(),this.queries.delete(e))},e.prototype.broadcastQueries=function(){this.onBroadcast&&this.onBroadcast(),this.queries.forEach(function(e){return e.notify()})},e.prototype.getLocalState=function(){return this.localState},e.prototype.getObservableFromLink=function(e,t,n,r){var i,a,o=this;void 0===r&&(r=null!==(i=null==t?void 0:t.queryDeduplication)&&void 0!==i?i:this.queryDeduplication);var s=this.transform(e).serverQuery;if(s){var u=this,c=u.inFlightLinkObservables,l=u.link,f={query:s,variables:n,operationName:e3(s)||void 0,context:this.prepareContext((0,en.pi)((0,en.pi)({},t),{forceFetch:!r}))};if(t=f.context,r){var d=c.get(s)||new Map;c.set(s,d);var h=nx(n);if(!(a=d.get(h))){var p=new nq([np(l,f)]);d.set(h,a=p),p.beforeNext(function(){d.delete(h)&&d.size<1&&c.delete(s)})}}else a=new nq([np(l,f)])}else a=new nq([eT.of({data:{}})]),t=this.prepareContext(t);var b=this.transform(e).clientQuery;return b&&(a=nM(a,function(e){return o.localState.runResolvers({document:b,remoteResult:e,context:t,variables:n})})),a},e.prototype.getResultsFromLink=function(e,t,n){var r=e.lastRequestId=this.generateRequestId(),i=this.cache.transformForLink(this.transform(e.document).document);return nM(this.getObservableFromLink(i,n.context,n.variables),function(a){var o=nA(a),s=o.length>0;if(r>=e.lastRequestId){if(s&&"none"===n.errorPolicy)throw e.markError(new tN.cA({graphQLErrors:o}));e.markResult(a,i,n,t),e.markReady()}var u={data:a.data,loading:!1,networkStatus:nZ.I.ready};return s&&"ignore"!==n.errorPolicy&&(u.errors=o,u.networkStatus=nZ.I.error),u},function(t){var n=(0,tN.MS)(t)?t:new tN.cA({networkError:t});throw r>=e.lastRequestId&&e.markError(n),n})},e.prototype.fetchQueryObservable=function(e,t,n){return this.fetchConcastWithInfo(e,t,n).concast},e.prototype.fetchConcastWithInfo=function(e,t,n){var r,i,a=this;void 0===n&&(n=nZ.I.loading);var o=this.transform(t.query).document,s=this.getVariables(o,t.variables),u=this.getQuery(e),c=this.defaultOptions.watchQuery,l=t.fetchPolicy,f=void 0===l?c&&c.fetchPolicy||"cache-first":l,d=t.errorPolicy,h=void 0===d?c&&c.errorPolicy||"none":d,p=t.returnPartialData,b=void 0!==p&&p,m=t.notifyOnNetworkStatusChange,g=void 0!==m&&m,v=t.context,y=void 0===v?{}:v,w=Object.assign({},t,{query:o,variables:s,fetchPolicy:f,errorPolicy:h,returnPartialData:b,notifyOnNetworkStatusChange:g,context:y}),_=function(e){w.variables=e;var r=a.fetchQueryByPolicy(u,w,n);return"standby"!==w.fetchPolicy&&r.sources.length>0&&u.observableQuery&&u.observableQuery.applyNextFetchPolicy("after-fetch",t),r},E=function(){return a.fetchCancelFns.delete(e)};if(this.fetchCancelFns.set(e,function(e){E(),setTimeout(function(){return r.cancel(e)})}),this.transform(w.query).hasClientExports)r=new nq(this.localState.addExportedVariables(w.query,w.variables,w.context).then(_).then(function(e){return e.sources})),i=!0;else{var S=_(w.variables);i=S.fromLink,r=new nq(S.sources)}return r.promise.then(E,E),{concast:r,fromLink:i}},e.prototype.refetchQueries=function(e){var t=this,n=e.updateCache,r=e.include,i=e.optimistic,a=void 0!==i&&i,o=e.removeOptimistic,s=void 0===o?a?nG("refetchQueries"):void 0:o,u=e.onQueryUpdated,c=new Map;r&&this.getObservableQueries(r).forEach(function(e,n){c.set(n,{oq:e,lastDiff:t.getQuery(n).getDiff()})});var l=new Map;return n&&this.cache.batch({update:n,optimistic:a&&s||!1,removeOptimistic:s,onWatchUpdated:function(e,t,n){var r=e.watcher instanceof r9&&e.watcher.observableQuery;if(r){if(u){c.delete(r.queryId);var i=u(r,t,n);return!0===i&&(i=r.refetch()),!1!==i&&l.set(r,i),i}null!==u&&c.set(r.queryId,{oq:r,lastDiff:n,diff:t})}}}),c.size&&c.forEach(function(e,n){var r,i=e.oq,a=e.lastDiff,o=e.diff;if(u){if(!o){var s=i.queryInfo;s.reset(),o=s.getDiff()}r=u(i,o,a)}u&&!0!==r||(r=i.refetch()),!1!==r&&l.set(i,r),n.indexOf("legacyOneTimeQuery")>=0&&t.stopQueryNoBroadcast(n)}),s&&this.cache.removeOptimistic(s),l},e.prototype.fetchQueryByPolicy=function(e,t,n){var r=this,i=t.query,a=t.variables,o=t.fetchPolicy,s=t.refetchWritePolicy,u=t.errorPolicy,c=t.returnPartialData,l=t.context,f=t.notifyOnNetworkStatusChange,d=e.networkStatus;e.init({document:this.transform(i).document,variables:a,networkStatus:n});var h=function(){return e.getDiff(a)},p=function(t,n){void 0===n&&(n=e.networkStatus||nZ.I.loading);var o=t.result;!__DEV__||c||(0,nm.D)(o,{})||n5(t.missing);var s=function(e){return eT.of((0,en.pi)({data:e,loading:(0,nZ.O)(n),networkStatus:n},t.complete?null:{partial:!0}))};return o&&r.transform(i).hasForcedResolvers?r.localState.runResolvers({document:i,remoteResult:{data:o},context:l,variables:a,onlyRunForcedResolvers:!0}).then(function(e){return s(e.data||void 0)}):"none"===u&&n===nZ.I.refetch&&Array.isArray(t.missing)?s(void 0):s(o)},b="no-cache"===o?0:n===nZ.I.refetch&&"merge"!==s?1:2,m=function(){return r.getResultsFromLink(e,b,{variables:a,context:l,fetchPolicy:o,errorPolicy:u})},g=f&&"number"==typeof d&&d!==n&&(0,nZ.O)(n);switch(o){default:case"cache-first":var v=h();if(v.complete)return{fromLink:!1,sources:[p(v,e.markReady())]};if(c||g)return{fromLink:!0,sources:[p(v),m()]};return{fromLink:!0,sources:[m()]};case"cache-and-network":var v=h();if(v.complete||c||g)return{fromLink:!0,sources:[p(v),m()]};return{fromLink:!0,sources:[m()]};case"cache-only":return{fromLink:!1,sources:[p(h(),e.markReady())]};case"network-only":if(g)return{fromLink:!0,sources:[p(h()),m()]};return{fromLink:!0,sources:[m()]};case"no-cache":if(g)return{fromLink:!0,sources:[p(e.getDiff()),m(),]};return{fromLink:!0,sources:[m()]};case"standby":return{fromLink:!1,sources:[]}}},e.prototype.getQuery=function(e){return e&&!this.queries.has(e)&&this.queries.set(e,new r9(this,e)),this.queries.get(e)},e.prototype.prepareContext=function(e){void 0===e&&(e={});var t=this.localState.prepareContext(e);return(0,en.pi)((0,en.pi)({},t),{clientAwareness:this.clientAwareness})},e}(),ir=__webpack_require__(14012),ii=!1,ia=function(){function e(e){var t=this;this.resetStoreCallbacks=[],this.clearStoreCallbacks=[];var n=e.uri,r=e.credentials,i=e.headers,a=e.cache,o=e.ssrMode,s=void 0!==o&&o,u=e.ssrForceFetchDelay,c=void 0===u?0:u,l=e.connectToDevTools,f=void 0===l?"object"==typeof window&&!window.__APOLLO_CLIENT__&&__DEV__:l,d=e.queryDeduplication,h=void 0===d||d,p=e.defaultOptions,b=e.assumeImmutableResults,m=void 0!==b&&b,g=e.resolvers,v=e.typeDefs,y=e.fragmentMatcher,w=e.name,_=e.version,E=e.link;if(E||(E=n?new nh({uri:n,credentials:r,headers:i}):ta.empty()),!a)throw __DEV__?new Q.ej("To initialize Apollo Client, you must specify a 'cache' property in the options object. \nFor more information, please visit: https://go.apollo.dev/c/docs"):new Q.ej(9);if(this.link=E,this.cache=a,this.disableNetworkFetches=s||c>0,this.queryDeduplication=h,this.defaultOptions=p||Object.create(null),this.typeDefs=v,c&&setTimeout(function(){return t.disableNetworkFetches=!1},c),this.watchQuery=this.watchQuery.bind(this),this.query=this.query.bind(this),this.mutate=this.mutate.bind(this),this.resetStore=this.resetStore.bind(this),this.reFetchObservableQueries=this.reFetchObservableQueries.bind(this),f&&"object"==typeof window&&(window.__APOLLO_CLIENT__=this),!ii&&f&&__DEV__&&(ii=!0,"undefined"!=typeof window&&window.document&&window.top===window.self&&!window.__APOLLO_DEVTOOLS_GLOBAL_HOOK__)){var S=window.navigator,k=S&&S.userAgent,x=void 0;"string"==typeof k&&(k.indexOf("Chrome/")>-1?x="https://chrome.google.com/webstore/detail/apollo-client-developer-t/jdkknkkbebbapilgoeccciglkfbmbnfm":k.indexOf("Firefox/")>-1&&(x="https://addons.mozilla.org/en-US/firefox/addon/apollo-developer-tools/")),x&&__DEV__&&Q.kG.log("Download the Apollo DevTools for a better development experience: "+x)}this.version=nb,this.localState=new r4({cache:a,client:this,resolvers:g,fragmentMatcher:y}),this.queryManager=new it({cache:this.cache,link:this.link,defaultOptions:this.defaultOptions,queryDeduplication:h,ssrMode:s,clientAwareness:{name:w,version:_},localState:this.localState,assumeImmutableResults:m,onBroadcast:f?function(){t.devToolsHookCb&&t.devToolsHookCb({action:{},state:{queries:t.queryManager.getQueryStore(),mutations:t.queryManager.mutationStore||{}},dataWithOptimisticResults:t.cache.extract(!0)})}:void 0})}return e.prototype.stop=function(){this.queryManager.stop()},e.prototype.watchQuery=function(e){return this.defaultOptions.watchQuery&&(e=(0,ir.J)(this.defaultOptions.watchQuery,e)),this.disableNetworkFetches&&("network-only"===e.fetchPolicy||"cache-and-network"===e.fetchPolicy)&&(e=(0,en.pi)((0,en.pi)({},e),{fetchPolicy:"cache-first"})),this.queryManager.watchQuery(e)},e.prototype.query=function(e){return this.defaultOptions.query&&(e=(0,ir.J)(this.defaultOptions.query,e)),__DEV__?(0,Q.kG)("cache-and-network"!==e.fetchPolicy,"The cache-and-network fetchPolicy does not work with client.query, because client.query can only return a single result. Please use client.watchQuery to receive multiple results from the cache and the network, or consider using a different fetchPolicy, such as cache-first or network-only."):(0,Q.kG)("cache-and-network"!==e.fetchPolicy,10),this.disableNetworkFetches&&"network-only"===e.fetchPolicy&&(e=(0,en.pi)((0,en.pi)({},e),{fetchPolicy:"cache-first"})),this.queryManager.query(e)},e.prototype.mutate=function(e){return this.defaultOptions.mutate&&(e=(0,ir.J)(this.defaultOptions.mutate,e)),this.queryManager.mutate(e)},e.prototype.subscribe=function(e){return this.queryManager.startGraphQLSubscription(e)},e.prototype.readQuery=function(e,t){return void 0===t&&(t=!1),this.cache.readQuery(e,t)},e.prototype.readFragment=function(e,t){return void 0===t&&(t=!1),this.cache.readFragment(e,t)},e.prototype.writeQuery=function(e){var t=this.cache.writeQuery(e);return!1!==e.broadcast&&this.queryManager.broadcastQueries(),t},e.prototype.writeFragment=function(e){var t=this.cache.writeFragment(e);return!1!==e.broadcast&&this.queryManager.broadcastQueries(),t},e.prototype.__actionHookForDevTools=function(e){this.devToolsHookCb=e},e.prototype.__requestRaw=function(e){return np(this.link,e)},e.prototype.resetStore=function(){var e=this;return Promise.resolve().then(function(){return e.queryManager.clearStore({discardWatches:!1})}).then(function(){return Promise.all(e.resetStoreCallbacks.map(function(e){return e()}))}).then(function(){return e.reFetchObservableQueries()})},e.prototype.clearStore=function(){var e=this;return Promise.resolve().then(function(){return e.queryManager.clearStore({discardWatches:!0})}).then(function(){return Promise.all(e.clearStoreCallbacks.map(function(e){return e()}))})},e.prototype.onResetStore=function(e){var t=this;return this.resetStoreCallbacks.push(e),function(){t.resetStoreCallbacks=t.resetStoreCallbacks.filter(function(t){return t!==e})}},e.prototype.onClearStore=function(e){var t=this;return this.clearStoreCallbacks.push(e),function(){t.clearStoreCallbacks=t.clearStoreCallbacks.filter(function(t){return t!==e})}},e.prototype.reFetchObservableQueries=function(e){return this.queryManager.reFetchObservableQueries(e)},e.prototype.refetchQueries=function(e){var t=this.queryManager.refetchQueries(e),n=[],r=[];t.forEach(function(e,t){n.push(t),r.push(e)});var i=Promise.all(r);return i.queries=n,i.results=r,i.catch(function(e){__DEV__&&Q.kG.debug("In client.refetchQueries, Promise.all promise rejected with error ".concat(e))}),i},e.prototype.getObservableQueries=function(e){return void 0===e&&(e="active"),this.queryManager.getObservableQueries(e)},e.prototype.extract=function(e){return this.cache.extract(e)},e.prototype.restore=function(e){return this.cache.restore(e)},e.prototype.addResolvers=function(e){this.localState.addResolvers(e)},e.prototype.setResolvers=function(e){this.localState.setResolvers(e)},e.prototype.getResolvers=function(){return this.localState.getResolvers()},e.prototype.setLocalStateFragmentMatcher=function(e){this.localState.setFragmentMatcher(e)},e.prototype.setLink=function(e){this.link=this.queryManager.link=e},e}(),io=function(){function e(){this.getFragmentDoc=rZ(eA)}return e.prototype.batch=function(e){var t,n=this,r="string"==typeof e.optimistic?e.optimistic:!1===e.optimistic?null:void 0;return this.performTransaction(function(){return t=e.update(n)},r),t},e.prototype.recordOptimisticTransaction=function(e,t){this.performTransaction(e,t)},e.prototype.transformDocument=function(e){return e},e.prototype.transformForLink=function(e){return e},e.prototype.identify=function(e){},e.prototype.gc=function(){return[]},e.prototype.modify=function(e){return!1},e.prototype.readQuery=function(e,t){return void 0===t&&(t=!!e.optimistic),this.read((0,en.pi)((0,en.pi)({},e),{rootId:e.id||"ROOT_QUERY",optimistic:t}))},e.prototype.readFragment=function(e,t){return void 0===t&&(t=!!e.optimistic),this.read((0,en.pi)((0,en.pi)({},e),{query:this.getFragmentDoc(e.fragment,e.fragmentName),rootId:e.id,optimistic:t}))},e.prototype.writeQuery=function(e){var t=e.id,n=e.data,r=(0,en._T)(e,["id","data"]);return this.write(Object.assign(r,{dataId:t||"ROOT_QUERY",result:n}))},e.prototype.writeFragment=function(e){var t=e.id,n=e.data,r=e.fragment,i=e.fragmentName,a=(0,en._T)(e,["id","data","fragment","fragmentName"]);return this.write(Object.assign(a,{query:this.getFragmentDoc(r,i),dataId:t,result:n}))},e.prototype.updateQuery=function(e,t){return this.batch({update:function(n){var r=n.readQuery(e),i=t(r);return null==i?r:(n.writeQuery((0,en.pi)((0,en.pi)({},e),{data:i})),i)}})},e.prototype.updateFragment=function(e,t){return this.batch({update:function(n){var r=n.readFragment(e),i=t(r);return null==i?r:(n.writeFragment((0,en.pi)((0,en.pi)({},e),{data:i})),i)}})},e}(),is=function(e){function t(n,r,i,a){var o,s=e.call(this,n)||this;if(s.message=n,s.path=r,s.query=i,s.variables=a,Array.isArray(s.path)){s.missing=s.message;for(var u=s.path.length-1;u>=0;--u)s.missing=((o={})[s.path[u]]=s.missing,o)}else s.missing=s.path;return s.__proto__=t.prototype,s}return(0,en.ZT)(t,e),t}(Error),iu=__webpack_require__(10542),ic=Object.prototype.hasOwnProperty;function il(e){return null==e}function id(e,t){var n=e.__typename,r=e.id,i=e._id;if("string"==typeof n&&(t&&(t.keyObject=il(r)?il(i)?void 0:{_id:i}:{id:r}),il(r)&&!il(i)&&(r=i),!il(r)))return"".concat(n,":").concat("number"==typeof r||"string"==typeof r?r:JSON.stringify(r))}var ih={dataIdFromObject:id,addTypename:!0,resultCaching:!0,canonizeResults:!1};function ip(e){return(0,n1.o)(ih,e)}function ib(e){var t=e.canonizeResults;return void 0===t?ih.canonizeResults:t}function im(e,t){return eD(t)?e.get(t.__ref,"__typename"):t&&t.__typename}var ig=/^[_a-z][_0-9a-z]*/i;function iv(e){var t=e.match(ig);return t?t[0]:e}function iy(e,t,n){return!!(0,eO.s)(t)&&((0,tP.k)(t)?t.every(function(t){return iy(e,t,n)}):e.selections.every(function(e){if(eQ(e)&&td(e,n)){var r=eX(e);return ic.call(t,r)&&(!e.selectionSet||iy(e.selectionSet,t[r],n))}return!0}))}function iw(e){return(0,eO.s)(e)&&!eD(e)&&!(0,tP.k)(e)}function i_(){return new tB}function iE(e,t){var n=eL(e4(e));return{fragmentMap:n,lookupFragment:function(e){var r=n[e];return!r&&t&&(r=t.lookup(e)),r||null}}}var iS=Object.create(null),ik=function(){return iS},ix=Object.create(null),iT=function(){function e(e,t){var n=this;this.policies=e,this.group=t,this.data=Object.create(null),this.rootIds=Object.create(null),this.refs=Object.create(null),this.getFieldValue=function(e,t){return(0,iu.J)(eD(e)?n.get(e.__ref,t):e&&e[t])},this.canRead=function(e){return eD(e)?n.has(e.__ref):"object"==typeof e},this.toReference=function(e,t){if("string"==typeof e)return eI(e);if(eD(e))return e;var r=n.policies.identify(e)[0];if(r){var i=eI(r);return t&&n.merge(r,e),i}}}return e.prototype.toObject=function(){return(0,en.pi)({},this.data)},e.prototype.has=function(e){return void 0!==this.lookup(e,!0)},e.prototype.get=function(e,t){if(this.group.depend(e,t),ic.call(this.data,e)){var n=this.data[e];if(n&&ic.call(n,t))return n[t]}return"__typename"===t&&ic.call(this.policies.rootTypenamesById,e)?this.policies.rootTypenamesById[e]:this instanceof iL?this.parent.get(e,t):void 0},e.prototype.lookup=function(e,t){return(t&&this.group.depend(e,"__exists"),ic.call(this.data,e))?this.data[e]:this instanceof iL?this.parent.lookup(e,t):this.policies.rootTypenamesById[e]?Object.create(null):void 0},e.prototype.merge=function(e,t){var n,r=this;eD(e)&&(e=e.__ref),eD(t)&&(t=t.__ref);var i="string"==typeof e?this.lookup(n=e):e,a="string"==typeof t?this.lookup(n=t):t;if(a){__DEV__?(0,Q.kG)("string"==typeof n,"store.merge expects a string ID"):(0,Q.kG)("string"==typeof n,1);var o=new tB(iI).merge(i,a);if(this.data[n]=o,o!==i&&(delete this.refs[n],this.group.caching)){var s=Object.create(null);i||(s.__exists=1),Object.keys(a).forEach(function(e){if(!i||i[e]!==o[e]){s[e]=1;var t=iv(e);t===e||r.policies.hasKeyArgs(o.__typename,t)||(s[t]=1),void 0!==o[e]||r instanceof iL||delete o[e]}}),s.__typename&&!(i&&i.__typename)&&this.policies.rootTypenamesById[n]===o.__typename&&delete s.__typename,Object.keys(s).forEach(function(e){return r.group.dirty(n,e)})}}},e.prototype.modify=function(e,t){var n=this,r=this.lookup(e);if(r){var i=Object.create(null),a=!1,o=!0,s={DELETE:iS,INVALIDATE:ix,isReference:eD,toReference:this.toReference,canRead:this.canRead,readField:function(t,r){return n.policies.readField("string"==typeof t?{fieldName:t,from:r||eI(e)}:t,{store:n})}};if(Object.keys(r).forEach(function(u){var c=iv(u),l=r[u];if(void 0!==l){var f="function"==typeof t?t:t[u]||t[c];if(f){var d=f===ik?iS:f((0,iu.J)(l),(0,en.pi)((0,en.pi)({},s),{fieldName:c,storeFieldName:u,storage:n.getStorage(e,u)}));d===ix?n.group.dirty(e,u):(d===iS&&(d=void 0),d!==l&&(i[u]=d,a=!0,l=d))}void 0!==l&&(o=!1)}}),a)return this.merge(e,i),o&&(this instanceof iL?this.data[e]=void 0:delete this.data[e],this.group.dirty(e,"__exists")),!0}return!1},e.prototype.delete=function(e,t,n){var r,i=this.lookup(e);if(i){var a=this.getFieldValue(i,"__typename"),o=t&&n?this.policies.getStoreFieldName({typename:a,fieldName:t,args:n}):t;return this.modify(e,o?((r={})[o]=ik,r):ik)}return!1},e.prototype.evict=function(e,t){var n=!1;return e.id&&(ic.call(this.data,e.id)&&(n=this.delete(e.id,e.fieldName,e.args)),this instanceof iL&&this!==t&&(n=this.parent.evict(e,t)||n),(e.fieldName||n)&&this.group.dirty(e.id,e.fieldName||"__exists")),n},e.prototype.clear=function(){this.replace(null)},e.prototype.extract=function(){var e=this,t=this.toObject(),n=[];return this.getRootIdSet().forEach(function(t){ic.call(e.policies.rootTypenamesById,t)||n.push(t)}),n.length&&(t.__META={extraRootIds:n.sort()}),t},e.prototype.replace=function(e){var t=this;if(Object.keys(this.data).forEach(function(n){e&&ic.call(e,n)||t.delete(n)}),e){var n=e.__META,r=(0,en._T)(e,["__META"]);Object.keys(r).forEach(function(e){t.merge(e,r[e])}),n&&n.extraRootIds.forEach(this.retain,this)}},e.prototype.retain=function(e){return this.rootIds[e]=(this.rootIds[e]||0)+1},e.prototype.release=function(e){if(this.rootIds[e]>0){var t=--this.rootIds[e];return t||delete this.rootIds[e],t}return 0},e.prototype.getRootIdSet=function(e){return void 0===e&&(e=new Set),Object.keys(this.rootIds).forEach(e.add,e),this instanceof iL?this.parent.getRootIdSet(e):Object.keys(this.policies.rootTypenamesById).forEach(e.add,e),e},e.prototype.gc=function(){var e=this,t=this.getRootIdSet(),n=this.toObject();t.forEach(function(r){ic.call(n,r)&&(Object.keys(e.findChildRefIds(r)).forEach(t.add,t),delete n[r])});var r=Object.keys(n);if(r.length){for(var i=this;i instanceof iL;)i=i.parent;r.forEach(function(e){return i.delete(e)})}return r},e.prototype.findChildRefIds=function(e){if(!ic.call(this.refs,e)){var t=this.refs[e]=Object.create(null),n=this.data[e];if(!n)return t;var r=new Set([n]);r.forEach(function(e){eD(e)&&(t[e.__ref]=!0),(0,eO.s)(e)&&Object.keys(e).forEach(function(t){var n=e[t];(0,eO.s)(n)&&r.add(n)})})}return this.refs[e]},e.prototype.makeCacheKey=function(){return this.group.keyMaker.lookupArray(arguments)},e}(),iM=function(){function e(e,t){void 0===t&&(t=null),this.caching=e,this.parent=t,this.d=null,this.resetCaching()}return e.prototype.resetCaching=function(){this.d=this.caching?rW():null,this.keyMaker=new n_(t_.mr)},e.prototype.depend=function(e,t){if(this.d){this.d(iO(e,t));var n=iv(t);n!==t&&this.d(iO(e,n)),this.parent&&this.parent.depend(e,t)}},e.prototype.dirty=function(e,t){this.d&&this.d.dirty(iO(e,t),"__exists"===t?"forget":"setDirty")},e}();function iO(e,t){return t+"#"+e}function iA(e,t){iD(e)&&e.group.depend(t,"__exists")}!function(e){var t=function(e){function t(t){var n=t.policies,r=t.resultCaching,i=void 0===r||r,a=t.seed,o=e.call(this,n,new iM(i))||this;return o.stump=new iC(o),o.storageTrie=new n_(t_.mr),a&&o.replace(a),o}return(0,en.ZT)(t,e),t.prototype.addLayer=function(e,t){return this.stump.addLayer(e,t)},t.prototype.removeLayer=function(){return this},t.prototype.getStorage=function(){return this.storageTrie.lookupArray(arguments)},t}(e);e.Root=t}(iT||(iT={}));var iL=function(e){function t(t,n,r,i){var a=e.call(this,n.policies,i)||this;return a.id=t,a.parent=n,a.replay=r,a.group=i,r(a),a}return(0,en.ZT)(t,e),t.prototype.addLayer=function(e,n){return new t(e,this,n,this.group)},t.prototype.removeLayer=function(e){var t=this,n=this.parent.removeLayer(e);return e===this.id?(this.group.caching&&Object.keys(this.data).forEach(function(e){var r=t.data[e],i=n.lookup(e);i?r?r!==i&&Object.keys(r).forEach(function(n){(0,nm.D)(r[n],i[n])||t.group.dirty(e,n)}):(t.group.dirty(e,"__exists"),Object.keys(i).forEach(function(n){t.group.dirty(e,n)})):t.delete(e)}),n):n===this.parent?this:n.addLayer(this.id,this.replay)},t.prototype.toObject=function(){return(0,en.pi)((0,en.pi)({},this.parent.toObject()),this.data)},t.prototype.findChildRefIds=function(t){var n=this.parent.findChildRefIds(t);return ic.call(this.data,t)?(0,en.pi)((0,en.pi)({},n),e.prototype.findChildRefIds.call(this,t)):n},t.prototype.getStorage=function(){for(var e=this.parent;e.parent;)e=e.parent;return e.getStorage.apply(e,arguments)},t}(iT),iC=function(e){function t(t){return e.call(this,"EntityStore.Stump",t,function(){},new iM(t.group.caching,t.group))||this}return(0,en.ZT)(t,e),t.prototype.removeLayer=function(){return this},t.prototype.merge=function(){return this.parent.merge.apply(this.parent,arguments)},t}(iL);function iI(e,t,n){var r=e[n],i=t[n];return(0,nm.D)(r,i)?r:i}function iD(e){return!!(e instanceof iT&&e.group.caching)}function iN(e){return[e.selectionSet,e.objectOrReference,e.context,e.context.canonizeResults,]}var iP=function(){function e(e){var t=this;this.knownResults=new(t_.mr?WeakMap:Map),this.config=(0,n1.o)(e,{addTypename:!1!==e.addTypename,canonizeResults:ib(e)}),this.canon=e.canon||new nk,this.executeSelectionSet=rZ(function(e){var n,r=e.context.canonizeResults,i=iN(e);i[3]=!r;var a=(n=t.executeSelectionSet).peek.apply(n,i);return a?r?(0,en.pi)((0,en.pi)({},a),{result:t.canon.admit(a.result)}):a:(iA(e.context.store,e.enclosingRef.__ref),t.execSelectionSetImpl(e))},{max:this.config.resultCacheMaxSize,keyArgs:iN,makeCacheKey:function(e,t,n,r){if(iD(n.store))return n.store.makeCacheKey(e,eD(t)?t.__ref:t,n.varString,r)}}),this.executeSubSelectedArray=rZ(function(e){return iA(e.context.store,e.enclosingRef.__ref),t.execSubSelectedArrayImpl(e)},{max:this.config.resultCacheMaxSize,makeCacheKey:function(e){var t=e.field,n=e.array,r=e.context;if(iD(r.store))return r.store.makeCacheKey(t,n,r.varString)}})}return e.prototype.resetCanon=function(){this.canon=new nk},e.prototype.diffQueryAgainstStore=function(e){var t,n=e.store,r=e.query,i=e.rootId,a=void 0===i?"ROOT_QUERY":i,o=e.variables,s=e.returnPartialData,u=void 0===s||s,c=e.canonizeResults,l=void 0===c?this.config.canonizeResults:c,f=this.config.cache.policies;o=(0,en.pi)((0,en.pi)({},e9(e6(r))),o);var d=eI(a),h=this.executeSelectionSet({selectionSet:e8(r).selectionSet,objectOrReference:d,enclosingRef:d,context:(0,en.pi)({store:n,query:r,policies:f,variables:o,varString:nx(o),canonizeResults:l},iE(r,this.config.fragments))});if(h.missing&&(t=[new is(iR(h.missing),h.missing,r,o)],!u))throw t[0];return{result:h.result,complete:!t,missing:t}},e.prototype.isFresh=function(e,t,n,r){if(iD(r.store)&&this.knownResults.get(e)===n){var i=this.executeSelectionSet.peek(n,t,r,this.canon.isKnown(e));if(i&&e===i.result)return!0}return!1},e.prototype.execSelectionSetImpl=function(e){var t,n=this,r=e.selectionSet,i=e.objectOrReference,a=e.enclosingRef,o=e.context;if(eD(i)&&!o.policies.rootTypenamesById[i.__ref]&&!o.store.has(i.__ref))return{result:this.canon.empty,missing:"Dangling reference to missing ".concat(i.__ref," object")};var s=o.variables,u=o.policies,c=o.store.getFieldValue(i,"__typename"),l=[],f=new tB;function d(e,n){var r;return e.missing&&(t=f.merge(t,((r={})[n]=e.missing,r))),e.result}this.config.addTypename&&"string"==typeof c&&!u.rootIdsByTypename[c]&&l.push({__typename:c});var h=new Set(r.selections);h.forEach(function(e){var r,p;if(td(e,s)){if(eQ(e)){var b=u.readField({fieldName:e.name.value,field:e,variables:o.variables,from:i},o),m=eX(e);void 0===b?nj.added(e)||(t=f.merge(t,((r={})[m]="Can't find field '".concat(e.name.value,"' on ").concat(eD(i)?i.__ref+" object":"object "+JSON.stringify(i,null,2)),r))):(0,tP.k)(b)?b=d(n.executeSubSelectedArray({field:e,array:b,enclosingRef:a,context:o}),m):e.selectionSet?null!=b&&(b=d(n.executeSelectionSet({selectionSet:e.selectionSet,objectOrReference:b,enclosingRef:eD(b)?b:a,context:o}),m)):o.canonizeResults&&(b=n.canon.pass(b)),void 0!==b&&l.push(((p={})[m]=b,p))}else{var g=eC(e,o.lookupFragment);if(!g&&e.kind===nL.h.FRAGMENT_SPREAD)throw __DEV__?new Q.ej("No fragment named ".concat(e.name.value)):new Q.ej(5);g&&u.fragmentMatches(g,c)&&g.selectionSet.selections.forEach(h.add,h)}}});var p={result:tF(l),missing:t},b=o.canonizeResults?this.canon.admit(p):(0,iu.J)(p);return b.result&&this.knownResults.set(b.result,r),b},e.prototype.execSubSelectedArrayImpl=function(e){var t,n=this,r=e.field,i=e.array,a=e.enclosingRef,o=e.context,s=new tB;function u(e,n){var r;return e.missing&&(t=s.merge(t,((r={})[n]=e.missing,r))),e.result}return r.selectionSet&&(i=i.filter(o.store.canRead)),i=i.map(function(e,t){return null===e?null:(0,tP.k)(e)?u(n.executeSubSelectedArray({field:r,array:e,enclosingRef:a,context:o}),t):r.selectionSet?u(n.executeSelectionSet({selectionSet:r.selectionSet,objectOrReference:e,enclosingRef:eD(e)?e:a,context:o}),t):(__DEV__&&ij(o.store,r,e),e)}),{result:o.canonizeResults?this.canon.admit(i):i,missing:t}},e}();function iR(e){try{JSON.stringify(e,function(e,t){if("string"==typeof t)throw t;return t})}catch(t){return t}}function ij(e,t,n){if(!t.selectionSet){var r=new Set([n]);r.forEach(function(n){(0,eO.s)(n)&&(__DEV__?(0,Q.kG)(!eD(n),"Missing selection set for object of type ".concat(im(e,n)," returned for query field ").concat(t.name.value)):(0,Q.kG)(!eD(n),6),Object.values(n).forEach(r.add,r))})}}function iF(e){var t=nG("stringifyForDisplay");return JSON.stringify(e,function(e,n){return void 0===n?t:n}).split(JSON.stringify(t)).join("")}var iY=Object.create(null);function iB(e){var t=JSON.stringify(e);return iY[t]||(iY[t]=Object.create(null))}function iU(e){var t=iB(e);return t.keyFieldsFn||(t.keyFieldsFn=function(t,n){var r=function(e,t){return n.readField(t,e)},i=n.keyObject=i$(e,function(e){var i=iW(n.storeObject,e,r);return void 0===i&&t!==n.storeObject&&ic.call(t,e[0])&&(i=iW(t,e,iG)),__DEV__?(0,Q.kG)(void 0!==i,"Missing field '".concat(e.join("."),"' while extracting keyFields from ").concat(JSON.stringify(t))):(0,Q.kG)(void 0!==i,2),i});return"".concat(n.typename,":").concat(JSON.stringify(i))})}function iH(e){var t=iB(e);return t.keyArgsFn||(t.keyArgsFn=function(t,n){var r=n.field,i=n.variables,a=n.fieldName,o=JSON.stringify(i$(e,function(e){var n=e[0],a=n.charAt(0);if("@"===a){if(r&&(0,tP.O)(r.directives)){var o=n.slice(1),s=r.directives.find(function(e){return e.name.value===o}),u=s&&eZ(s,i);return u&&iW(u,e.slice(1))}return}if("$"===a){var c=n.slice(1);if(i&&ic.call(i,c)){var l=e.slice(0);return l[0]=c,iW(i,l)}return}if(t)return iW(t,e)}));return(t||"{}"!==o)&&(a+=":"+o),a})}function i$(e,t){var n=new tB;return iz(e).reduce(function(e,r){var i,a=t(r);if(void 0!==a){for(var o=r.length-1;o>=0;--o)a=((i={})[r[o]]=a,i);e=n.merge(e,a)}return e},Object.create(null))}function iz(e){var t=iB(e);if(!t.paths){var n=t.paths=[],r=[];e.forEach(function(t,i){(0,tP.k)(t)?(iz(t).forEach(function(e){return n.push(r.concat(e))}),r.length=0):(r.push(t),(0,tP.k)(e[i+1])||(n.push(r.slice(0)),r.length=0))})}return t.paths}function iG(e,t){return e[t]}function iW(e,t,n){return n=n||iG,iK(t.reduce(function e(t,r){return(0,tP.k)(t)?t.map(function(t){return e(t,r)}):t&&n(t,r)},e))}function iK(e){return(0,eO.s)(e)?(0,tP.k)(e)?e.map(iK):i$(Object.keys(e).sort(),function(t){return iW(e,t)}):e}function iV(e){return void 0!==e.args?e.args:e.field?eZ(e.field,e.variables):null}eK.setStringify(nx);var iq=function(){},iZ=function(e,t){return t.fieldName},iX=function(e,t,n){return(0,n.mergeObjects)(e,t)},iJ=function(e,t){return t},iQ=function(){function e(e){this.config=e,this.typePolicies=Object.create(null),this.toBeAdded=Object.create(null),this.supertypeMap=new Map,this.fuzzySubtypes=new Map,this.rootIdsByTypename=Object.create(null),this.rootTypenamesById=Object.create(null),this.usingPossibleTypes=!1,this.config=(0,en.pi)({dataIdFromObject:id},e),this.cache=this.config.cache,this.setRootTypename("Query"),this.setRootTypename("Mutation"),this.setRootTypename("Subscription"),e.possibleTypes&&this.addPossibleTypes(e.possibleTypes),e.typePolicies&&this.addTypePolicies(e.typePolicies)}return e.prototype.identify=function(e,t){var n,r,i=this,a=t&&(t.typename||(null===(n=t.storeObject)||void 0===n?void 0:n.__typename))||e.__typename;if(a===this.rootTypenamesById.ROOT_QUERY)return["ROOT_QUERY"];for(var o=t&&t.storeObject||e,s=(0,en.pi)((0,en.pi)({},t),{typename:a,storeObject:o,readField:t&&t.readField||function(){var e=i0(arguments,o);return i.readField(e,{store:i.cache.data,variables:e.variables})}}),u=a&&this.getTypePolicy(a),c=u&&u.keyFn||this.config.dataIdFromObject;c;){var l=c((0,en.pi)((0,en.pi)({},e),o),s);if((0,tP.k)(l))c=iU(l);else{r=l;break}}return r=r?String(r):void 0,s.keyObject?[r,s.keyObject]:[r]},e.prototype.addTypePolicies=function(e){var t=this;Object.keys(e).forEach(function(n){var r=e[n],i=r.queryType,a=r.mutationType,o=r.subscriptionType,s=(0,en._T)(r,["queryType","mutationType","subscriptionType"]);i&&t.setRootTypename("Query",n),a&&t.setRootTypename("Mutation",n),o&&t.setRootTypename("Subscription",n),ic.call(t.toBeAdded,n)?t.toBeAdded[n].push(s):t.toBeAdded[n]=[s]})},e.prototype.updateTypePolicy=function(e,t){var n=this,r=this.getTypePolicy(e),i=t.keyFields,a=t.fields;function o(e,t){e.merge="function"==typeof t?t:!0===t?iX:!1===t?iJ:e.merge}o(r,t.merge),r.keyFn=!1===i?iq:(0,tP.k)(i)?iU(i):"function"==typeof i?i:r.keyFn,a&&Object.keys(a).forEach(function(t){var r=n.getFieldPolicy(e,t,!0),i=a[t];if("function"==typeof i)r.read=i;else{var s=i.keyArgs,u=i.read,c=i.merge;r.keyFn=!1===s?iZ:(0,tP.k)(s)?iH(s):"function"==typeof s?s:r.keyFn,"function"==typeof u&&(r.read=u),o(r,c)}r.read&&r.merge&&(r.keyFn=r.keyFn||iZ)})},e.prototype.setRootTypename=function(e,t){void 0===t&&(t=e);var n="ROOT_"+e.toUpperCase(),r=this.rootTypenamesById[n];t!==r&&(__DEV__?(0,Q.kG)(!r||r===e,"Cannot change root ".concat(e," __typename more than once")):(0,Q.kG)(!r||r===e,3),r&&delete this.rootIdsByTypename[r],this.rootIdsByTypename[t]=n,this.rootTypenamesById[n]=t)},e.prototype.addPossibleTypes=function(e){var t=this;this.usingPossibleTypes=!0,Object.keys(e).forEach(function(n){t.getSupertypeSet(n,!0),e[n].forEach(function(e){t.getSupertypeSet(e,!0).add(n);var r=e.match(ig);r&&r[0]===e||t.fuzzySubtypes.set(e,RegExp(e))})})},e.prototype.getTypePolicy=function(e){var t=this;if(!ic.call(this.typePolicies,e)){var n=this.typePolicies[e]=Object.create(null);n.fields=Object.create(null);var r=this.supertypeMap.get(e);r&&r.size&&r.forEach(function(e){var r=t.getTypePolicy(e),i=r.fields;Object.assign(n,(0,en._T)(r,["fields"])),Object.assign(n.fields,i)})}var i=this.toBeAdded[e];return i&&i.length&&i.splice(0).forEach(function(n){t.updateTypePolicy(e,n)}),this.typePolicies[e]},e.prototype.getFieldPolicy=function(e,t,n){if(e){var r=this.getTypePolicy(e).fields;return r[t]||n&&(r[t]=Object.create(null))}},e.prototype.getSupertypeSet=function(e,t){var n=this.supertypeMap.get(e);return!n&&t&&this.supertypeMap.set(e,n=new Set),n},e.prototype.fragmentMatches=function(e,t,n,r){var i=this;if(!e.typeCondition)return!0;if(!t)return!1;var a=e.typeCondition.name.value;if(t===a)return!0;if(this.usingPossibleTypes&&this.supertypeMap.has(a))for(var o=this.getSupertypeSet(t,!0),s=[o],u=function(e){var t=i.getSupertypeSet(e,!1);t&&t.size&&0>s.indexOf(t)&&s.push(t)},c=!!(n&&this.fuzzySubtypes.size),l=!1,f=0;f1?a:t}:(r=(0,en.pi)({},i),ic.call(r,"from")||(r.from=t)),__DEV__&&void 0===r.from&&__DEV__&&Q.kG.warn("Undefined 'from' passed to readField with arguments ".concat(iF(Array.from(e)))),void 0===r.variables&&(r.variables=n),r}function i2(e){return function(t,n){if((0,tP.k)(t)||(0,tP.k)(n))throw __DEV__?new Q.ej("Cannot automatically merge arrays"):new Q.ej(4);if((0,eO.s)(t)&&(0,eO.s)(n)){var r=e.getFieldValue(t,"__typename"),i=e.getFieldValue(n,"__typename");if(r&&i&&r!==i)return n;if(eD(t)&&iw(n))return e.merge(t.__ref,n),t;if(iw(t)&&eD(n))return e.merge(t,n.__ref),n;if(iw(t)&&iw(n))return(0,en.pi)((0,en.pi)({},t),n)}return n}}function i3(e,t,n){var r="".concat(t).concat(n),i=e.flavors.get(r);return i||e.flavors.set(r,i=e.clientOnly===t&&e.deferred===n?e:(0,en.pi)((0,en.pi)({},e),{clientOnly:t,deferred:n})),i}var i4=function(){function e(e,t,n){this.cache=e,this.reader=t,this.fragments=n}return e.prototype.writeToStore=function(e,t){var n=this,r=t.query,i=t.result,a=t.dataId,o=t.variables,s=t.overwrite,u=e2(r),c=i_();o=(0,en.pi)((0,en.pi)({},e9(u)),o);var l=(0,en.pi)((0,en.pi)({store:e,written:Object.create(null),merge:function(e,t){return c.merge(e,t)},variables:o,varString:nx(o)},iE(r,this.fragments)),{overwrite:!!s,incomingById:new Map,clientOnly:!1,deferred:!1,flavors:new Map}),f=this.processSelectionSet({result:i||Object.create(null),dataId:a,selectionSet:u.selectionSet,mergeTree:{map:new Map},context:l});if(!eD(f))throw __DEV__?new Q.ej("Could not identify object ".concat(JSON.stringify(i))):new Q.ej(7);return l.incomingById.forEach(function(t,r){var i=t.storeObject,a=t.mergeTree,o=t.fieldNodeSet,s=eI(r);if(a&&a.map.size){var u=n.applyMerges(a,s,i,l);if(eD(u))return;i=u}if(__DEV__&&!l.overwrite){var c=Object.create(null);o.forEach(function(e){e.selectionSet&&(c[e.name.value]=!0)});var f=function(e){return!0===c[iv(e)]},d=function(e){var t=a&&a.map.get(e);return Boolean(t&&t.info&&t.info.merge)};Object.keys(i).forEach(function(e){f(e)&&!d(e)&&at(s,i,e,l.store)})}e.merge(r,i)}),e.retain(f.__ref),f},e.prototype.processSelectionSet=function(e){var t=this,n=e.dataId,r=e.result,i=e.selectionSet,a=e.context,o=e.mergeTree,s=this.cache.policies,u=Object.create(null),c=n&&s.rootTypenamesById[n]||eJ(r,i,a.fragmentMap)||n&&a.store.get(n,"__typename");"string"==typeof c&&(u.__typename=c);var l=function(){var e=i0(arguments,u,a.variables);if(eD(e.from)){var t=a.incomingById.get(e.from.__ref);if(t){var n=s.readField((0,en.pi)((0,en.pi)({},e),{from:t.storeObject}),a);if(void 0!==n)return n}}return s.readField(e,a)},f=new Set;this.flattenFields(i,r,a,c).forEach(function(e,n){var i,a=r[eX(n)];if(f.add(n),void 0!==a){var d=s.getStoreFieldName({typename:c,fieldName:n.name.value,field:n,variables:e.variables}),h=i5(o,d),p=t.processFieldValue(a,n,n.selectionSet?i3(e,!1,!1):e,h),b=void 0;n.selectionSet&&(eD(p)||iw(p))&&(b=l("__typename",p));var m=s.getMergeFunction(c,n.name.value,b);m?h.info={field:n,typename:c,merge:m}:i7(o,d),u=e.merge(u,((i={})[d]=p,i))}else __DEV__&&!e.clientOnly&&!e.deferred&&!nj.added(n)&&!s.getReadFunction(c,n.name.value)&&__DEV__&&Q.kG.error("Missing field '".concat(eX(n),"' while writing result ").concat(JSON.stringify(r,null,2)).substring(0,1e3))});try{var d=s.identify(r,{typename:c,selectionSet:i,fragmentMap:a.fragmentMap,storeObject:u,readField:l}),h=d[0],p=d[1];n=n||h,p&&(u=a.merge(u,p))}catch(b){if(!n)throw b}if("string"==typeof n){var m=eI(n),g=a.written[n]||(a.written[n]=[]);if(g.indexOf(i)>=0||(g.push(i),this.reader&&this.reader.isFresh(r,m,i,a)))return m;var v=a.incomingById.get(n);return v?(v.storeObject=a.merge(v.storeObject,u),v.mergeTree=i8(v.mergeTree,o),f.forEach(function(e){return v.fieldNodeSet.add(e)})):a.incomingById.set(n,{storeObject:u,mergeTree:i9(o)?void 0:o,fieldNodeSet:f}),m}return u},e.prototype.processFieldValue=function(e,t,n,r){var i=this;return t.selectionSet&&null!==e?(0,tP.k)(e)?e.map(function(e,a){var o=i.processFieldValue(e,t,n,i5(r,a));return i7(r,a),o}):this.processSelectionSet({result:e,selectionSet:t.selectionSet,context:n,mergeTree:r}):__DEV__?nJ(e):e},e.prototype.flattenFields=function(e,t,n,r){void 0===r&&(r=eJ(t,e,n.fragmentMap));var i=new Map,a=this.cache.policies,o=new n_(!1);return function e(s,u){var c=o.lookup(s,u.clientOnly,u.deferred);c.visited||(c.visited=!0,s.selections.forEach(function(o){if(td(o,n.variables)){var s=u.clientOnly,c=u.deferred;if(!(s&&c)&&(0,tP.O)(o.directives)&&o.directives.forEach(function(e){var t=e.name.value;if("client"===t&&(s=!0),"defer"===t){var r=eZ(e,n.variables);r&&!1===r.if||(c=!0)}}),eQ(o)){var l=i.get(o);l&&(s=s&&l.clientOnly,c=c&&l.deferred),i.set(o,i3(n,s,c))}else{var f=eC(o,n.lookupFragment);if(!f&&o.kind===nL.h.FRAGMENT_SPREAD)throw __DEV__?new Q.ej("No fragment named ".concat(o.name.value)):new Q.ej(8);f&&a.fragmentMatches(f,r,t,n.variables)&&e(f.selectionSet,i3(n,s,c))}}}))}(e,n),i},e.prototype.applyMerges=function(e,t,n,r,i){var a=this;if(e.map.size&&!eD(n)){var o,s,u=!(0,tP.k)(n)&&(eD(t)||iw(t))?t:void 0,c=n;u&&!i&&(i=[eD(u)?u.__ref:u]);var l=function(e,t){return(0,tP.k)(e)?"number"==typeof t?e[t]:void 0:r.store.getFieldValue(e,String(t))};e.map.forEach(function(e,t){var n=l(u,t),o=l(c,t);if(void 0!==o){i&&i.push(t);var f=a.applyMerges(e,n,o,r,i);f!==o&&(s=s||new Map).set(t,f),i&&(0,Q.kG)(i.pop()===t)}}),s&&(n=(0,tP.k)(c)?c.slice(0):(0,en.pi)({},c),s.forEach(function(e,t){n[t]=e}))}return e.info?this.cache.policies.runMergeFunction(t,n,e.info,r,i&&(o=r.store).getStorage.apply(o,i)):n},e}(),i6=[];function i5(e,t){var n=e.map;return n.has(t)||n.set(t,i6.pop()||{map:new Map}),n.get(t)}function i8(e,t){if(e===t||!t||i9(t))return e;if(!e||i9(e))return t;var n=e.info&&t.info?(0,en.pi)((0,en.pi)({},e.info),t.info):e.info||t.info,r=e.map.size&&t.map.size,i=r?new Map:e.map.size?e.map:t.map,a={info:n,map:i};if(r){var o=new Set(t.map.keys());e.map.forEach(function(e,n){a.map.set(n,i8(e,t.map.get(n))),o.delete(n)}),o.forEach(function(n){a.map.set(n,i8(t.map.get(n),e.map.get(n)))})}return a}function i9(e){return!e||!(e.info||e.map.size)}function i7(e,t){var n=e.map,r=n.get(t);r&&i9(r)&&(i6.push(r),n.delete(t))}var ae=new Set;function at(e,t,n,r){var i=function(e){var t=r.getFieldValue(e,n);return"object"==typeof t&&t},a=i(e);if(a){var o=i(t);if(!(!o||eD(a)||(0,nm.D)(a,o)||Object.keys(a).every(function(e){return void 0!==r.getFieldValue(o,e)}))){var s=r.getFieldValue(e,"__typename")||r.getFieldValue(t,"__typename"),u=iv(n),c="".concat(s,".").concat(u);if(!ae.has(c)){ae.add(c);var l=[];(0,tP.k)(a)||(0,tP.k)(o)||[a,o].forEach(function(e){var t=r.getFieldValue(e,"__typename");"string"!=typeof t||l.includes(t)||l.push(t)}),__DEV__&&Q.kG.warn("Cache data may be lost when replacing the ".concat(u," field of a ").concat(s," object.\n\nThis could cause additional (usually avoidable) network requests to fetch data that were otherwise cached.\n\nTo address this problem (which is not a bug in Apollo Client), ").concat(l.length?"either ensure all objects of type "+l.join(" and ")+" have an ID or a custom merge function, or ":"","define a custom merge function for the ").concat(c," field, so InMemoryCache can safely merge these objects:\n\n existing: ").concat(JSON.stringify(a).slice(0,1e3),"\n incoming: ").concat(JSON.stringify(o).slice(0,1e3),"\n\nFor more information about these options, please refer to the documentation:\n\n * Ensuring entity objects have IDs: https://go.apollo.dev/c/generating-unique-identifiers\n * Defining custom merge functions: https://go.apollo.dev/c/merging-non-normalized-objects\n"))}}}}var an=function(e){function t(t){void 0===t&&(t={});var n=e.call(this)||this;return n.watches=new Set,n.typenameDocumentCache=new Map,n.makeVar=r2,n.txCount=0,n.config=ip(t),n.addTypename=!!n.config.addTypename,n.policies=new iQ({cache:n,dataIdFromObject:n.config.dataIdFromObject,possibleTypes:n.config.possibleTypes,typePolicies:n.config.typePolicies}),n.init(),n}return(0,en.ZT)(t,e),t.prototype.init=function(){var e=this.data=new iT.Root({policies:this.policies,resultCaching:this.config.resultCaching});this.optimisticData=e.stump,this.resetResultCache()},t.prototype.resetResultCache=function(e){var t=this,n=this.storeReader,r=this.config.fragments;this.storeWriter=new i4(this,this.storeReader=new iP({cache:this,addTypename:this.addTypename,resultCacheMaxSize:this.config.resultCacheMaxSize,canonizeResults:ib(this.config),canon:e?void 0:n&&n.canon,fragments:r}),r),this.maybeBroadcastWatch=rZ(function(e,n){return t.broadcastWatch(e,n)},{max:this.config.resultCacheMaxSize,makeCacheKey:function(e){var n=e.optimistic?t.optimisticData:t.data;if(iD(n)){var r=e.optimistic,i=e.id,a=e.variables;return n.makeCacheKey(e.query,e.callback,nx({optimistic:r,id:i,variables:a}))}}}),new Set([this.data.group,this.optimisticData.group,]).forEach(function(e){return e.resetCaching()})},t.prototype.restore=function(e){return this.init(),e&&this.data.replace(e),this},t.prototype.extract=function(e){return void 0===e&&(e=!1),(e?this.optimisticData:this.data).extract()},t.prototype.read=function(e){var t=e.returnPartialData,n=void 0!==t&&t;try{return this.storeReader.diffQueryAgainstStore((0,en.pi)((0,en.pi)({},e),{store:e.optimistic?this.optimisticData:this.data,config:this.config,returnPartialData:n})).result||null}catch(r){if(r instanceof is)return null;throw r}},t.prototype.write=function(e){try{return++this.txCount,this.storeWriter.writeToStore(this.data,e)}finally{--this.txCount||!1===e.broadcast||this.broadcastWatches()}},t.prototype.modify=function(e){if(ic.call(e,"id")&&!e.id)return!1;var t=e.optimistic?this.optimisticData:this.data;try{return++this.txCount,t.modify(e.id||"ROOT_QUERY",e.fields)}finally{--this.txCount||!1===e.broadcast||this.broadcastWatches()}},t.prototype.diff=function(e){return this.storeReader.diffQueryAgainstStore((0,en.pi)((0,en.pi)({},e),{store:e.optimistic?this.optimisticData:this.data,rootId:e.id||"ROOT_QUERY",config:this.config}))},t.prototype.watch=function(e){var t=this;return this.watches.size||r0(this),this.watches.add(e),e.immediate&&this.maybeBroadcastWatch(e),function(){t.watches.delete(e)&&!t.watches.size&&r1(t),t.maybeBroadcastWatch.forget(e)}},t.prototype.gc=function(e){nx.reset();var t=this.optimisticData.gc();return e&&!this.txCount&&(e.resetResultCache?this.resetResultCache(e.resetResultIdentities):e.resetResultIdentities&&this.storeReader.resetCanon()),t},t.prototype.retain=function(e,t){return(t?this.optimisticData:this.data).retain(e)},t.prototype.release=function(e,t){return(t?this.optimisticData:this.data).release(e)},t.prototype.identify=function(e){if(eD(e))return e.__ref;try{return this.policies.identify(e)[0]}catch(t){__DEV__&&Q.kG.warn(t)}},t.prototype.evict=function(e){if(!e.id){if(ic.call(e,"id"))return!1;e=(0,en.pi)((0,en.pi)({},e),{id:"ROOT_QUERY"})}try{return++this.txCount,this.optimisticData.evict(e,this.data)}finally{--this.txCount||!1===e.broadcast||this.broadcastWatches()}},t.prototype.reset=function(e){var t=this;return this.init(),nx.reset(),e&&e.discardWatches?(this.watches.forEach(function(e){return t.maybeBroadcastWatch.forget(e)}),this.watches.clear(),r1(this)):this.broadcastWatches(),Promise.resolve()},t.prototype.removeOptimistic=function(e){var t=this.optimisticData.removeLayer(e);t!==this.optimisticData&&(this.optimisticData=t,this.broadcastWatches())},t.prototype.batch=function(e){var t,n=this,r=e.update,i=e.optimistic,a=void 0===i||i,o=e.removeOptimistic,s=e.onWatchUpdated,u=function(e){var i=n,a=i.data,o=i.optimisticData;++n.txCount,e&&(n.data=n.optimisticData=e);try{return t=r(n)}finally{--n.txCount,n.data=a,n.optimisticData=o}},c=new Set;return s&&!this.txCount&&this.broadcastWatches((0,en.pi)((0,en.pi)({},e),{onWatchUpdated:function(e){return c.add(e),!1}})),"string"==typeof a?this.optimisticData=this.optimisticData.addLayer(a,u):!1===a?u(this.data):u(),"string"==typeof o&&(this.optimisticData=this.optimisticData.removeLayer(o)),s&&c.size?(this.broadcastWatches((0,en.pi)((0,en.pi)({},e),{onWatchUpdated:function(e,t){var n=s.call(this,e,t);return!1!==n&&c.delete(e),n}})),c.size&&c.forEach(function(e){return n.maybeBroadcastWatch.dirty(e)})):this.broadcastWatches(e),t},t.prototype.performTransaction=function(e,t){return this.batch({update:e,optimistic:t||null!==t})},t.prototype.transformDocument=function(e){if(this.addTypename){var t=this.typenameDocumentCache.get(e);return t||(t=nj(e),this.typenameDocumentCache.set(e,t),this.typenameDocumentCache.set(t,t)),t}return e},t.prototype.transformForLink=function(e){var t=this.config.fragments;return t?t.transform(e):e},t.prototype.broadcastWatches=function(e){var t=this;this.txCount||this.watches.forEach(function(n){return t.maybeBroadcastWatch(n,e)})},t.prototype.broadcastWatch=function(e,t){var n=e.lastDiff,r=this.diff(e);(!t||(e.optimistic&&"string"==typeof t.optimistic&&(r.fromOptimisticTransaction=!0),!t.onWatchUpdated||!1!==t.onWatchUpdated.call(this,e,r,n)))&&(n&&(0,nm.D)(n.result,r.result)||e.callback(e.lastDiff=r,n))},t}(io),ar={possibleTypes:{ApproveJobProposalSpecPayload:["ApproveJobProposalSpecSuccess","JobAlreadyExistsError","NotFoundError"],BridgePayload:["Bridge","NotFoundError"],CancelJobProposalSpecPayload:["CancelJobProposalSpecSuccess","NotFoundError"],ChainPayload:["Chain","NotFoundError"],CreateAPITokenPayload:["CreateAPITokenSuccess","InputErrors"],CreateBridgePayload:["CreateBridgeSuccess"],CreateCSAKeyPayload:["CSAKeyExistsError","CreateCSAKeySuccess"],CreateFeedsManagerChainConfigPayload:["CreateFeedsManagerChainConfigSuccess","InputErrors","NotFoundError"],CreateFeedsManagerPayload:["CreateFeedsManagerSuccess","DuplicateFeedsManagerError","InputErrors","NotFoundError","SingleFeedsManagerError"],CreateJobPayload:["CreateJobSuccess","InputErrors"],CreateOCR2KeyBundlePayload:["CreateOCR2KeyBundleSuccess"],CreateOCRKeyBundlePayload:["CreateOCRKeyBundleSuccess"],CreateP2PKeyPayload:["CreateP2PKeySuccess"],DeleteAPITokenPayload:["DeleteAPITokenSuccess","InputErrors"],DeleteBridgePayload:["DeleteBridgeConflictError","DeleteBridgeInvalidNameError","DeleteBridgeSuccess","NotFoundError"],DeleteCSAKeyPayload:["DeleteCSAKeySuccess","NotFoundError"],DeleteFeedsManagerChainConfigPayload:["DeleteFeedsManagerChainConfigSuccess","NotFoundError"],DeleteJobPayload:["DeleteJobSuccess","NotFoundError"],DeleteOCR2KeyBundlePayload:["DeleteOCR2KeyBundleSuccess","NotFoundError"],DeleteOCRKeyBundlePayload:["DeleteOCRKeyBundleSuccess","NotFoundError"],DeleteP2PKeyPayload:["DeleteP2PKeySuccess","NotFoundError"],DeleteVRFKeyPayload:["DeleteVRFKeySuccess","NotFoundError"],DismissJobErrorPayload:["DismissJobErrorSuccess","NotFoundError"],Error:["CSAKeyExistsError","DeleteBridgeConflictError","DeleteBridgeInvalidNameError","DuplicateFeedsManagerError","InputError","JobAlreadyExistsError","NotFoundError","RunJobCannotRunError","SingleFeedsManagerError"],EthTransactionPayload:["EthTransaction","NotFoundError"],FeaturesPayload:["Features"],FeedsManagerPayload:["FeedsManager","NotFoundError"],GetSQLLoggingPayload:["SQLLogging"],GlobalLogLevelPayload:["GlobalLogLevel"],JobPayload:["Job","NotFoundError"],JobProposalPayload:["JobProposal","NotFoundError"],JobRunPayload:["JobRun","NotFoundError"],JobSpec:["BlockHeaderFeederSpec","BlockhashStoreSpec","BootstrapSpec","CronSpec","DirectRequestSpec","FluxMonitorSpec","GatewaySpec","KeeperSpec","OCR2Spec","OCRSpec","StandardCapabilitiesSpec","VRFSpec","WebhookSpec","WorkflowSpec"],NodePayload:["Node","NotFoundError"],PaginatedPayload:["BridgesPayload","ChainsPayload","EthTransactionAttemptsPayload","EthTransactionsPayload","JobRunsPayload","JobsPayload","NodesPayload"],RejectJobProposalSpecPayload:["NotFoundError","RejectJobProposalSpecSuccess"],RunJobPayload:["NotFoundError","RunJobCannotRunError","RunJobSuccess"],SetGlobalLogLevelPayload:["InputErrors","SetGlobalLogLevelSuccess"],SetSQLLoggingPayload:["SetSQLLoggingSuccess"],SetServicesLogLevelsPayload:["InputErrors","SetServicesLogLevelsSuccess"],UpdateBridgePayload:["NotFoundError","UpdateBridgeSuccess"],UpdateFeedsManagerChainConfigPayload:["InputErrors","NotFoundError","UpdateFeedsManagerChainConfigSuccess"],UpdateFeedsManagerPayload:["InputErrors","NotFoundError","UpdateFeedsManagerSuccess"],UpdateJobProposalSpecDefinitionPayload:["NotFoundError","UpdateJobProposalSpecDefinitionSuccess"],UpdatePasswordPayload:["InputErrors","UpdatePasswordSuccess"],VRFKeyPayload:["NotFoundError","VRFKeySuccess"]}};let ai=ar;var aa=(r=void 0,location.origin),ao=new nh({uri:"".concat(aa,"/query"),credentials:"include"}),as=new ia({cache:new an({possibleTypes:ai.possibleTypes}),link:ao});if(a.Z.locale(o),u().defaultFormat="YYYY-MM-DD h:mm:ss A","undefined"!=typeof document){var au,ac,al=f().hydrate;ac=X,al(c.createElement(et,{client:as},c.createElement(d.zj,null,c.createElement(i.MuiThemeProvider,{theme:J.r},c.createElement(ac,null)))),document.getElementById("root"))}})()})(); \ No newline at end of file diff --git a/core/web/assets/main.84f90f8fc23465846aa7.js.gz b/core/web/assets/main.d9fae2bacda0e72ac5ae.js.gz similarity index 88% rename from core/web/assets/main.84f90f8fc23465846aa7.js.gz rename to core/web/assets/main.d9fae2bacda0e72ac5ae.js.gz index 7fcd5de9137..c3b560db80e 100644 Binary files a/core/web/assets/main.84f90f8fc23465846aa7.js.gz and b/core/web/assets/main.d9fae2bacda0e72ac5ae.js.gz differ diff --git a/core/web/loader/chain.go b/core/web/loader/chain.go index 215c643f3c5..54fccf9cf9e 100644 --- a/core/web/loader/chain.go +++ b/core/web/loader/chain.go @@ -5,8 +5,7 @@ import ( "slices" "github.com/graph-gophers/dataloader" - - "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/chains" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" ) @@ -25,20 +24,32 @@ func (b *chainBatcher) loadByIDs(ctx context.Context, keys dataloader.Keys) []*d keyOrder[key.String()] = ix } - var cs []types.ChainStatus - relayers := b.app.GetRelayers().Slice() + var cs []types.ChainStatusWithID + relayersMap, err := b.app.GetRelayers().GetIDToRelayerMap() + if err != nil { + return []*dataloader.Result{{Data: nil, Error: err}} + } - for _, r := range relayers { - s, err := r.GetChainStatus(ctx) + for k, v := range relayersMap { + s, err := v.GetChainStatus(ctx) if err != nil { return []*dataloader.Result{{Data: nil, Error: err}} } if slices.Contains(chainIDs, s.ID) { - cs = append(cs, s) + cs = append(cs, types.ChainStatusWithID{ + ChainStatus: s, + RelayID: k, + }) } } + // todo: future improvements to handle multiple chains with same id + if len(cs) > len(keys) { + b.app.GetLogger().Warn("Found multiple chain with same id") + return []*dataloader.Result{{Data: nil, Error: chains.ErrMultipleChainFound}} + } + results := make([]*dataloader.Result, len(keys)) for _, c := range cs { ix, ok := keyOrder[c.ID] diff --git a/core/web/loader/getters.go b/core/web/loader/getters.go index 27a39181ff8..33aba17db36 100644 --- a/core/web/loader/getters.go +++ b/core/web/loader/getters.go @@ -7,8 +7,7 @@ import ( "github.com/pkg/errors" "go.uber.org/multierr" - commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" - + commonTypes "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/services/feeds" @@ -21,7 +20,7 @@ import ( var ErrInvalidType = errors.New("invalid type") // GetChainByID fetches the chain by it's id. -func GetChainByID(ctx context.Context, id string) (*commontypes.ChainStatus, error) { +func GetChainByID(ctx context.Context, id string) (*commonTypes.ChainStatusWithID, error) { ldr := For(ctx) thunk := ldr.ChainsByIDLoader.Load(ctx, dataloader.StringKey(id)) @@ -30,7 +29,7 @@ func GetChainByID(ctx context.Context, id string) (*commontypes.ChainStatus, err return nil, err } - chain, ok := result.(commontypes.ChainStatus) + chain, ok := result.(commonTypes.ChainStatusWithID) if !ok { return nil, ErrInvalidType } diff --git a/core/web/loader/loader_test.go b/core/web/loader/loader_test.go index 5e22c9afef6..0e88f67c444 100644 --- a/core/web/loader/loader_test.go +++ b/core/web/loader/loader_test.go @@ -13,11 +13,13 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/chains" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtxmgrmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr/mocks" evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" + "github.com/smartcontractkit/chainlink/v2/core/services/relay" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" coremocks "github.com/smartcontractkit/chainlink/v2/core/internal/mocks" @@ -46,12 +48,18 @@ func TestLoader_Chains(t *testing.T) { config2, err := chain2.TOMLString() require.NoError(t, err) - app.On("GetRelayers").Return(&chainlinkmocks.FakeRelayerChainInteroperators{Relayers: []loop.Relayer{ - testutils2.MockRelayer{ChainStatus: commontypes.ChainStatus{ + app.On("GetRelayers").Return(&chainlinkmocks.FakeRelayerChainInteroperators{Relayers: map[commontypes.RelayID]loop.Relayer{ + commontypes.RelayID{ + Network: relay.NetworkEVM, + ChainID: "1", + }: testutils2.MockRelayer{ChainStatus: commontypes.ChainStatus{ ID: "1", Enabled: true, Config: config1, - }}, testutils2.MockRelayer{ChainStatus: commontypes.ChainStatus{ + }}, commontypes.RelayID{ + Network: relay.NetworkEVM, + ChainID: "2", + }: testutils2.MockRelayer{ChainStatus: commontypes.ChainStatus{ ID: "2", Enabled: true, Config: config2, @@ -65,11 +73,17 @@ func TestLoader_Chains(t *testing.T) { assert.Len(t, results, 3) require.NoError(t, err) - want2 := commontypes.ChainStatus{ID: "2", Enabled: true, Config: config2} - assert.Equal(t, want2, results[0].Data.(commontypes.ChainStatus)) + want2 := types.ChainStatusWithID{ + ChainStatus: commontypes.ChainStatus{ID: "2", Enabled: true, Config: config2}, + RelayID: commontypes.RelayID{Network: relay.NetworkEVM, ChainID: "2"}, + } + assert.Equal(t, want2, results[0].Data.(types.ChainStatusWithID)) - want1 := commontypes.ChainStatus{ID: "1", Enabled: true, Config: config1} - assert.Equal(t, want1, results[1].Data.(commontypes.ChainStatus)) + want1 := types.ChainStatusWithID{ + ChainStatus: commontypes.ChainStatus{ID: "1", Enabled: true, Config: config1}, + RelayID: commontypes.RelayID{Network: relay.NetworkEVM, ChainID: "1"}, + } + assert.Equal(t, want1, results[1].Data.(types.ChainStatusWithID)) assert.Nil(t, results[2].Data) assert.Error(t, results[2].Error) assert.ErrorIs(t, results[2].Error, chains.ErrNotFound) diff --git a/core/web/resolver/chain.go b/core/web/resolver/chain.go index 32e9a8caac3..22dfcf39e92 100644 --- a/core/web/resolver/chain.go +++ b/core/web/resolver/chain.go @@ -2,20 +2,19 @@ package resolver import ( "github.com/graph-gophers/graphql-go" - - "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink/v2/common/types" ) // ChainResolver resolves the Chain type. type ChainResolver struct { - chain types.ChainStatus + chain types.ChainStatusWithID } -func NewChain(chain types.ChainStatus) *ChainResolver { +func NewChain(chain types.ChainStatusWithID) *ChainResolver { return &ChainResolver{chain: chain} } -func NewChains(chains []types.ChainStatus) []*ChainResolver { +func NewChains(chains []types.ChainStatusWithID) []*ChainResolver { var resolvers []*ChainResolver for _, c := range chains { resolvers = append(resolvers, NewChain(c)) @@ -39,12 +38,17 @@ func (r *ChainResolver) Config() string { return r.chain.Config } +// Network resolves the chain's network field +func (r *ChainResolver) Network() string { + return r.chain.Network +} + type ChainPayloadResolver struct { - chain types.ChainStatus + chain types.ChainStatusWithID NotFoundErrorUnionType } -func NewChainPayload(chain types.ChainStatus, err error) *ChainPayloadResolver { +func NewChainPayload(chain types.ChainStatusWithID, err error) *ChainPayloadResolver { e := NotFoundErrorUnionType{err: err, message: "chain not found", isExpectedErrorFn: nil} return &ChainPayloadResolver{chain: chain, NotFoundErrorUnionType: e} @@ -59,11 +63,11 @@ func (r *ChainPayloadResolver) ToChain() (*ChainResolver, bool) { } type ChainsPayloadResolver struct { - chains []types.ChainStatus + chains []types.ChainStatusWithID total int32 } -func NewChainsPayload(chains []types.ChainStatus, total int32) *ChainsPayloadResolver { +func NewChainsPayload(chains []types.ChainStatusWithID, total int32) *ChainsPayloadResolver { return &ChainsPayloadResolver{chains: chains, total: total} } diff --git a/core/web/resolver/chain_test.go b/core/web/resolver/chain_test.go index 75d7e36a5b5..fed817df456 100644 --- a/core/web/resolver/chain_test.go +++ b/core/web/resolver/chain_test.go @@ -3,9 +3,11 @@ package resolver import ( "context" "encoding/json" + "errors" "fmt" "testing" + gqlerrors "github.com/graph-gophers/graphql-go/errors" "github.com/pelletier/go-toml/v2" "github.com/stretchr/testify/require" @@ -15,6 +17,7 @@ import ( evmtoml "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" chainlinkmocks "github.com/smartcontractkit/chainlink/v2/core/services/chainlink/mocks" + "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/web/testutils" ) @@ -28,6 +31,7 @@ func TestResolver_Chains(t *testing.T) { id enabled config + network } metadata { total @@ -87,8 +91,11 @@ ResendAfterThreshold = '1h0m0s' chainConfToml, err2 := chainConf.TOMLString() require.NoError(t, err2) - f.App.On("GetRelayers").Return(&chainlinkmocks.FakeRelayerChainInteroperators{Relayers: []loop.Relayer{ - testutils.MockRelayer{ChainStatus: commontypes.ChainStatus{ + f.App.On("GetRelayers").Return(&chainlinkmocks.FakeRelayerChainInteroperators{Relayers: map[commontypes.RelayID]loop.Relayer{ + commontypes.RelayID{ + Network: relay.NetworkEVM, + ChainID: chainID.String(), + }: testutils.MockRelayer{ChainStatus: commontypes.ChainStatus{ ID: chainID.String(), Enabled: *chain.Enabled, Config: chainConfToml, @@ -102,7 +109,8 @@ ResendAfterThreshold = '1h0m0s' "results": [{ "id": "1", "enabled": true, - "config": %s + "config": %s, + "network": "evm" }], "metadata": { "total": 1 @@ -115,7 +123,7 @@ ResendAfterThreshold = '1h0m0s' name: "no chains", authenticated: true, before: func(ctx context.Context, f *gqlTestFramework) { - f.App.On("GetRelayers").Return(&chainlinkmocks.FakeRelayerChainInteroperators{Relayers: []loop.Relayer{}}) + f.App.On("GetRelayers").Return(&chainlinkmocks.FakeRelayerChainInteroperators{Relayers: map[commontypes.RelayID]loop.Relayer{}}) }, query: query, result: ` @@ -143,6 +151,7 @@ func TestResolver_Chain(t *testing.T) { id enabled config + network } ... on NotFoundError { code @@ -188,17 +197,31 @@ ResendAfterThreshold = '1h0m0s' configTOMLEscaped, err := json.Marshal(configTOML) require.NoError(t, err) + multipleChainError := errors.New("multiple chains found with the same chain ID") testCases := []GQLTestCase{ unauthorizedTestCase(GQLTestCase{query: query}, "chain"), { name: "success", authenticated: true, before: func(ctx context.Context, f *gqlTestFramework) { - f.App.On("EVMORM").Return(f.Mocks.evmORM) - f.Mocks.evmORM.PutChains(evmtoml.EVMConfig{ - ChainID: &chainID, + chainConf := evmtoml.EVMConfig{ Chain: chain, - }) + ChainID: &chainID, + } + + chainConfToml, err2 := chainConf.TOMLString() + require.NoError(t, err2) + + f.App.On("GetRelayers").Return(&chainlinkmocks.FakeRelayerChainInteroperators{Relayers: map[commontypes.RelayID]loop.Relayer{ + commontypes.RelayID{ + Network: relay.NetworkEVM, + ChainID: chainID.String(), + }: testutils.MockRelayer{ChainStatus: commontypes.ChainStatus{ + ID: chainID.String(), + Enabled: chainConf.IsEnabled(), + Config: chainConfToml, + }}, + }}) }, query: query, result: fmt.Sprintf(` @@ -206,7 +229,8 @@ ResendAfterThreshold = '1h0m0s' "chain": { "id": "1", "enabled": true, - "config": %s + "config": %s, + "network": "evm" } }`, configTOMLEscaped), }, @@ -214,7 +238,7 @@ ResendAfterThreshold = '1h0m0s' name: "not found error", authenticated: true, before: func(ctx context.Context, f *gqlTestFramework) { - f.App.On("EVMORM").Return(f.Mocks.evmORM) + f.App.On("GetRelayers").Return(&chainlinkmocks.FakeRelayerChainInteroperators{Relayers: map[commontypes.RelayID]loop.Relayer{}}) }, query: query, result: ` @@ -225,6 +249,48 @@ ResendAfterThreshold = '1h0m0s' } }`, }, + { + name: "multiple chain with same chainID found error", + authenticated: true, + before: func(ctx context.Context, f *gqlTestFramework) { + chainConf := evmtoml.EVMConfig{ + Chain: chain, + ChainID: &chainID, + } + + chainConfToml, err2 := chainConf.TOMLString() + require.NoError(t, err2) + + f.App.On("GetRelayers").Return(&chainlinkmocks.FakeRelayerChainInteroperators{Relayers: map[commontypes.RelayID]loop.Relayer{ + commontypes.RelayID{ + Network: relay.NetworkEVM, + ChainID: chainID.String(), + }: testutils.MockRelayer{ChainStatus: commontypes.ChainStatus{ + ID: chainID.String(), + Enabled: chainConf.IsEnabled(), + Config: chainConfToml, + }}, + commontypes.RelayID{ + Network: relay.NetworkAptos, + ChainID: chainID.String(), + }: testutils.MockRelayer{ChainStatus: commontypes.ChainStatus{ + ID: chainID.String(), + Enabled: chainConf.IsEnabled(), + Config: chainConfToml, + }}, + }}) + }, + query: query, + result: "null", + errors: []*gqlerrors.QueryError{ + { + Extensions: nil, + ResolverError: multipleChainError, + Path: []interface{}{"chain"}, + Message: multipleChainError.Error(), + }, + }, + }, } RunGQLTests(t, testCases) diff --git a/core/web/resolver/cosmos_key.go b/core/web/resolver/cosmos_key.go new file mode 100644 index 00000000000..42a0464b97b --- /dev/null +++ b/core/web/resolver/cosmos_key.go @@ -0,0 +1,43 @@ +package resolver + +import ( + "github.com/graph-gophers/graphql-go" + + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/cosmoskey" +) + +type CosmosKeyResolver struct { + key cosmoskey.Key +} + +func NewCosmosKey(key cosmoskey.Key) *CosmosKeyResolver { + return &CosmosKeyResolver{key: key} +} + +func NewCosmosKeys(keys []cosmoskey.Key) []*CosmosKeyResolver { + var resolvers []*CosmosKeyResolver + + for _, k := range keys { + resolvers = append(resolvers, NewCosmosKey(k)) + } + + return resolvers +} + +func (r *CosmosKeyResolver) ID() graphql.ID { + return graphql.ID(r.key.PublicKeyStr()) +} + +// -- GetCosmosKeys Query -- + +type CosmosKeysPayloadResolver struct { + keys []cosmoskey.Key +} + +func NewCosmosKeysPayload(keys []cosmoskey.Key) *CosmosKeysPayloadResolver { + return &CosmosKeysPayloadResolver{keys: keys} +} + +func (r *CosmosKeysPayloadResolver) Results() []*CosmosKeyResolver { + return NewCosmosKeys(r.keys) +} diff --git a/core/web/resolver/cosmos_key_test.go b/core/web/resolver/cosmos_key_test.go new file mode 100644 index 00000000000..9068e453626 --- /dev/null +++ b/core/web/resolver/cosmos_key_test.go @@ -0,0 +1,74 @@ +package resolver + +import ( + "context" + "errors" + "fmt" + "testing" + + gqlerrors "github.com/graph-gophers/graphql-go/errors" + + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/keystest" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/cosmoskey" +) + +func TestResolver_CosmosKeys(t *testing.T) { + t.Parallel() + + query := ` + query GetCosmosKeys { + cosmosKeys { + results { + id + } + } + }` + k := cosmoskey.MustNewInsecure(keystest.NewRandReaderFromSeed(1)) + result := fmt.Sprintf(` + { + "cosmosKeys": { + "results": [ + { + "id": "%s" + } + ] + } + }`, k.PublicKeyStr()) + gError := errors.New("error") + + testCases := []GQLTestCase{ + unauthorizedTestCase(GQLTestCase{query: query}, "cosmosKeys"), + { + name: "success", + authenticated: true, + before: func(ctx context.Context, f *gqlTestFramework) { + f.Mocks.cosmos.On("GetAll").Return([]cosmoskey.Key{k}, nil) + f.Mocks.keystore.On("Cosmos").Return(f.Mocks.cosmos) + f.App.On("GetKeyStore").Return(f.Mocks.keystore) + }, + query: query, + result: result, + }, + { + name: "no keys returned by GetAll", + authenticated: true, + before: func(ctx context.Context, f *gqlTestFramework) { + f.Mocks.cosmos.On("GetAll").Return([]cosmoskey.Key{}, gError) + f.Mocks.keystore.On("Cosmos").Return(f.Mocks.cosmos) + f.App.On("GetKeyStore").Return(f.Mocks.keystore) + }, + query: query, + result: `null`, + errors: []*gqlerrors.QueryError{ + { + Extensions: nil, + ResolverError: gError, + Path: []interface{}{"cosmosKeys"}, + Message: gError.Error(), + }, + }, + }, + } + + RunGQLTests(t, testCases) +} diff --git a/core/web/resolver/eth_key_test.go b/core/web/resolver/eth_key_test.go index 55cdc230bd2..9002f17fce6 100644 --- a/core/web/resolver/eth_key_test.go +++ b/core/web/resolver/eth_key_test.go @@ -21,6 +21,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" + "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/web/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" @@ -107,8 +108,11 @@ func TestResolver_ETHKeys(t *testing.T) { f.Mocks.chain.On("BalanceMonitor").Return(f.Mocks.balM) f.Mocks.chain.On("Config").Return(f.Mocks.scfg) f.Mocks.relayerChainInterops.EVMChains = legacyEVMChains - f.Mocks.relayerChainInterops.Relayers = []loop.Relayer{ - testutils.MockRelayer{ + f.Mocks.relayerChainInterops.Relayers = map[types.RelayID]loop.Relayer{ + types.RelayID{ + Network: relay.NetworkEVM, + ChainID: "12", + }: testutils.MockRelayer{ ChainStatus: types.ChainStatus{ ID: "12", Enabled: true, @@ -165,8 +169,11 @@ func TestResolver_ETHKeys(t *testing.T) { f.Mocks.ethKs.On("GetAll", mock.Anything).Return(keys, nil) f.Mocks.relayerChainInterops.EVMChains = f.Mocks.legacyEVMChains f.Mocks.evmORM.PutChains(toml.EVMConfig{ChainID: &chainID}) - f.Mocks.relayerChainInterops.Relayers = []loop.Relayer{ - testutils.MockRelayer{ + f.Mocks.relayerChainInterops.Relayers = map[types.RelayID]loop.Relayer{ + types.RelayID{ + Network: relay.NetworkEVM, + ChainID: "12", + }: testutils.MockRelayer{ ChainStatus: types.ChainStatus{ ID: "12", Enabled: true, @@ -325,8 +332,11 @@ func TestResolver_ETHKeys(t *testing.T) { f.Mocks.ethClient.On("LINKBalance", mock.Anything, address, linkAddr).Return(commonassets.NewLinkFromJuels(12), gError) f.Mocks.legacyEVMChains.On("Get", states[0].EVMChainID.String()).Return(f.Mocks.chain, nil) f.Mocks.relayerChainInterops.EVMChains = f.Mocks.legacyEVMChains - f.Mocks.relayerChainInterops.Relayers = []loop.Relayer{ - testutils.MockRelayer{ + f.Mocks.relayerChainInterops.Relayers = map[types.RelayID]loop.Relayer{ + types.RelayID{ + Network: relay.NetworkEVM, + ChainID: "12", + }: testutils.MockRelayer{ ChainStatus: types.ChainStatus{ ID: "12", Enabled: true, @@ -390,8 +400,11 @@ func TestResolver_ETHKeys(t *testing.T) { f.Mocks.legacyEVMChains.On("Get", states[0].EVMChainID.String()).Return(f.Mocks.chain, nil) f.Mocks.relayerChainInterops.EVMChains = f.Mocks.legacyEVMChains f.Mocks.evmORM.PutChains(toml.EVMConfig{ChainID: &chainID}) - f.Mocks.relayerChainInterops.Relayers = []loop.Relayer{ - testutils.MockRelayer{ + f.Mocks.relayerChainInterops.Relayers = map[types.RelayID]loop.Relayer{ + types.RelayID{ + Network: relay.NetworkEVM, + ChainID: "12", + }: testutils.MockRelayer{ ChainStatus: types.ChainStatus{ ID: "12", Enabled: true, diff --git a/core/web/resolver/eth_transaction_test.go b/core/web/resolver/eth_transaction_test.go index 690eea30b50..5844c50fde3 100644 --- a/core/web/resolver/eth_transaction_test.go +++ b/core/web/resolver/eth_transaction_test.go @@ -22,6 +22,7 @@ import ( evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" chainlinkmocks "github.com/smartcontractkit/chainlink/v2/core/services/chainlink/mocks" + "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/web/testutils" ) @@ -93,8 +94,11 @@ func TestResolver_EthTransaction(t *testing.T) { f.App.On("TxmStorageService").Return(f.Mocks.txmStore) f.Mocks.evmORM.PutChains(toml.EVMConfig{ChainID: &chainID}) f.App.On("GetRelayers").Return(&chainlinkmocks.FakeRelayerChainInteroperators{ - Relayers: []loop.Relayer{ - testutils.MockRelayer{ChainStatus: types.ChainStatus{ + Relayers: map[types.RelayID]loop.Relayer{ + types.RelayID{ + Network: relay.NetworkEVM, + ChainID: "22", + }: testutils.MockRelayer{ChainStatus: types.ChainStatus{ ID: "22", Enabled: true, Config: "", @@ -158,8 +162,11 @@ func TestResolver_EthTransaction(t *testing.T) { f.App.On("TxmStorageService").Return(f.Mocks.txmStore) f.Mocks.evmORM.PutChains(toml.EVMConfig{ChainID: &chainID}) f.App.On("GetRelayers").Return(&chainlinkmocks.FakeRelayerChainInteroperators{ - Relayers: []loop.Relayer{ - testutils.MockRelayer{ChainStatus: types.ChainStatus{ + Relayers: map[types.RelayID]loop.Relayer{ + types.RelayID{ + Network: relay.NetworkEVM, + ChainID: "22", + }: testutils.MockRelayer{ChainStatus: types.ChainStatus{ ID: "22", Enabled: true, Config: "", diff --git a/core/web/resolver/feeds_manager_chain_config_test.go b/core/web/resolver/feeds_manager_chain_config_test.go index bd4c1e05aeb..957583dbb7d 100644 --- a/core/web/resolver/feeds_manager_chain_config_test.go +++ b/core/web/resolver/feeds_manager_chain_config_test.go @@ -46,33 +46,37 @@ func Test_CreateFeedsManagerChainConfig(t *testing.T) { } } }` - variables = map[string]interface{}{ - "input": map[string]interface{}{ - "feedsManagerID": stringutils.FromInt64(mgrID), - "chainID": chainID, - "chainType": "EVM", - "accountAddr": accountAddr, - "accountAddrPubKey": acctAddrPubKey, - "adminAddr": adminAddr, - "fluxMonitorEnabled": false, - "ocr1Enabled": true, - "ocr1IsBootstrap": false, - "ocr1P2PPeerID": peerID.String, - "ocr1KeyBundleID": keyBundleID.String, - "ocr2Enabled": true, - "ocr2IsBootstrap": false, - "ocr2P2PPeerID": peerID.String, - "ocr2KeyBundleID": keyBundleID.String, - "ocr2Plugins": `{"commit":true,"execute":true,"median":false,"mercury":true,"rebalancer":true}`, - "ocr2ForwarderAddress": forwarderAddr, - }, + + withVariables = func(chainType string) map[string]interface{} { + variables := map[string]interface{}{ + "input": map[string]interface{}{ + "feedsManagerID": stringutils.FromInt64(mgrID), + "chainID": chainID, + "chainType": chainType, + "accountAddr": accountAddr, + "accountAddrPubKey": acctAddrPubKey, + "adminAddr": adminAddr, + "fluxMonitorEnabled": false, + "ocr1Enabled": true, + "ocr1IsBootstrap": false, + "ocr1P2PPeerID": peerID.String, + "ocr1KeyBundleID": keyBundleID.String, + "ocr2Enabled": true, + "ocr2IsBootstrap": false, + "ocr2P2PPeerID": peerID.String, + "ocr2KeyBundleID": keyBundleID.String, + "ocr2Plugins": `{"commit":true,"execute":true,"median":false,"mercury":true,"rebalancer":true}`, + "ocr2ForwarderAddress": forwarderAddr, + }, + } + return variables } ) testCases := []GQLTestCase{ - unauthorizedTestCase(GQLTestCase{query: mutation, variables: variables}, "createFeedsManagerChainConfig"), + unauthorizedTestCase(GQLTestCase{query: mutation, variables: withVariables("EVM")}, "createFeedsManagerChainConfig"), { - name: "success", + name: "success EVM", authenticated: true, before: func(ctx context.Context, f *gqlTestFramework) { f.App.On("GetFeedsService").Return(f.Mocks.feedsSvc) @@ -136,7 +140,232 @@ func Test_CreateFeedsManagerChainConfig(t *testing.T) { }, nil) }, query: mutation, - variables: variables, + variables: withVariables("EVM"), + result: ` + { + "createFeedsManagerChainConfig": { + "chainConfig": { + "id": "1" + } + } + }`, + }, + { + name: "success Solana", + authenticated: true, + before: func(ctx context.Context, f *gqlTestFramework) { + f.App.On("GetFeedsService").Return(f.Mocks.feedsSvc) + f.Mocks.feedsSvc.On("CreateChainConfig", mock.Anything, feeds.ChainConfig{ + FeedsManagerID: mgrID, + ChainType: feeds.ChainTypeSolana, + ChainID: chainID, + AccountAddress: accountAddr, + AccountAddressPublicKey: null.StringFrom(acctAddrPubKey), + AdminAddress: adminAddr, + FluxMonitorConfig: feeds.FluxMonitorConfig{ + Enabled: false, + }, + OCR1Config: feeds.OCR1Config{ + Enabled: true, + P2PPeerID: peerID, + KeyBundleID: keyBundleID, + }, + OCR2Config: feeds.OCR2ConfigModel{ + Enabled: true, + P2PPeerID: peerID, + KeyBundleID: keyBundleID, + ForwarderAddress: null.StringFrom(forwarderAddr), + Plugins: feeds.Plugins{ + Commit: true, + Execute: true, + Median: false, + Mercury: true, + Rebalancer: true, + }, + }, + }).Return(cfgID, nil) + f.Mocks.feedsSvc.On("GetChainConfig", mock.Anything, cfgID).Return(&feeds.ChainConfig{ + ID: cfgID, + ChainType: feeds.ChainTypeSolana, + ChainID: chainID, + AccountAddress: accountAddr, + AccountAddressPublicKey: null.StringFrom(acctAddrPubKey), + AdminAddress: adminAddr, + FluxMonitorConfig: feeds.FluxMonitorConfig{ + Enabled: false, + }, + OCR1Config: feeds.OCR1Config{ + Enabled: true, + P2PPeerID: peerID, + KeyBundleID: keyBundleID, + }, + OCR2Config: feeds.OCR2ConfigModel{ + Enabled: true, + P2PPeerID: peerID, + KeyBundleID: keyBundleID, + ForwarderAddress: null.StringFrom(forwarderAddr), + Plugins: feeds.Plugins{ + Commit: true, + Execute: true, + Median: false, + Mercury: true, + Rebalancer: true, + }, + }, + }, nil) + }, + query: mutation, + variables: withVariables("SOLANA"), + result: ` + { + "createFeedsManagerChainConfig": { + "chainConfig": { + "id": "1" + } + } + }`, + }, + { + name: "success Starknet", + authenticated: true, + before: func(ctx context.Context, f *gqlTestFramework) { + f.App.On("GetFeedsService").Return(f.Mocks.feedsSvc) + f.Mocks.feedsSvc.On("CreateChainConfig", mock.Anything, feeds.ChainConfig{ + FeedsManagerID: mgrID, + ChainType: feeds.ChainTypeStarknet, + ChainID: chainID, + AccountAddress: accountAddr, + AccountAddressPublicKey: null.StringFrom(acctAddrPubKey), + AdminAddress: adminAddr, + FluxMonitorConfig: feeds.FluxMonitorConfig{ + Enabled: false, + }, + OCR1Config: feeds.OCR1Config{ + Enabled: true, + P2PPeerID: peerID, + KeyBundleID: keyBundleID, + }, + OCR2Config: feeds.OCR2ConfigModel{ + Enabled: true, + P2PPeerID: peerID, + KeyBundleID: keyBundleID, + ForwarderAddress: null.StringFrom(forwarderAddr), + Plugins: feeds.Plugins{ + Commit: true, + Execute: true, + Median: false, + Mercury: true, + Rebalancer: true, + }, + }, + }).Return(cfgID, nil) + f.Mocks.feedsSvc.On("GetChainConfig", mock.Anything, cfgID).Return(&feeds.ChainConfig{ + ID: cfgID, + ChainType: feeds.ChainTypeStarknet, + ChainID: chainID, + AccountAddress: accountAddr, + AccountAddressPublicKey: null.StringFrom(acctAddrPubKey), + AdminAddress: adminAddr, + FluxMonitorConfig: feeds.FluxMonitorConfig{ + Enabled: false, + }, + OCR1Config: feeds.OCR1Config{ + Enabled: true, + P2PPeerID: peerID, + KeyBundleID: keyBundleID, + }, + OCR2Config: feeds.OCR2ConfigModel{ + Enabled: true, + P2PPeerID: peerID, + KeyBundleID: keyBundleID, + ForwarderAddress: null.StringFrom(forwarderAddr), + Plugins: feeds.Plugins{ + Commit: true, + Execute: true, + Median: false, + Mercury: true, + Rebalancer: true, + }, + }, + }, nil) + }, + query: mutation, + variables: withVariables("STARKNET"), + result: ` + { + "createFeedsManagerChainConfig": { + "chainConfig": { + "id": "1" + } + } + }`, + }, + { + name: "success APTOS", + authenticated: true, + before: func(ctx context.Context, f *gqlTestFramework) { + f.App.On("GetFeedsService").Return(f.Mocks.feedsSvc) + f.Mocks.feedsSvc.On("CreateChainConfig", mock.Anything, feeds.ChainConfig{ + FeedsManagerID: mgrID, + ChainType: feeds.ChainTypeAptos, + ChainID: chainID, + AccountAddress: accountAddr, + AccountAddressPublicKey: null.StringFrom(acctAddrPubKey), + AdminAddress: adminAddr, + FluxMonitorConfig: feeds.FluxMonitorConfig{ + Enabled: false, + }, + OCR1Config: feeds.OCR1Config{ + Enabled: true, + P2PPeerID: peerID, + KeyBundleID: keyBundleID, + }, + OCR2Config: feeds.OCR2ConfigModel{ + Enabled: true, + P2PPeerID: peerID, + KeyBundleID: keyBundleID, + ForwarderAddress: null.StringFrom(forwarderAddr), + Plugins: feeds.Plugins{ + Commit: true, + Execute: true, + Median: false, + Mercury: true, + Rebalancer: true, + }, + }, + }).Return(cfgID, nil) + f.Mocks.feedsSvc.On("GetChainConfig", mock.Anything, cfgID).Return(&feeds.ChainConfig{ + ID: cfgID, + ChainType: feeds.ChainTypeAptos, + ChainID: chainID, + AccountAddress: accountAddr, + AccountAddressPublicKey: null.StringFrom(acctAddrPubKey), + AdminAddress: adminAddr, + FluxMonitorConfig: feeds.FluxMonitorConfig{ + Enabled: false, + }, + OCR1Config: feeds.OCR1Config{ + Enabled: true, + P2PPeerID: peerID, + KeyBundleID: keyBundleID, + }, + OCR2Config: feeds.OCR2ConfigModel{ + Enabled: true, + P2PPeerID: peerID, + KeyBundleID: keyBundleID, + ForwarderAddress: null.StringFrom(forwarderAddr), + Plugins: feeds.Plugins{ + Commit: true, + Execute: true, + Median: false, + Mercury: true, + Rebalancer: true, + }, + }, + }, nil) + }, + query: mutation, + variables: withVariables("APTOS"), result: ` { "createFeedsManagerChainConfig": { @@ -154,7 +383,7 @@ func Test_CreateFeedsManagerChainConfig(t *testing.T) { f.Mocks.feedsSvc.On("CreateChainConfig", mock.Anything, mock.IsType(feeds.ChainConfig{})).Return(int64(0), sql.ErrNoRows) }, query: mutation, - variables: variables, + variables: withVariables("EVM"), result: ` { "createFeedsManagerChainConfig": { @@ -172,7 +401,7 @@ func Test_CreateFeedsManagerChainConfig(t *testing.T) { f.Mocks.feedsSvc.On("GetChainConfig", mock.Anything, cfgID).Return(nil, sql.ErrNoRows) }, query: mutation, - variables: variables, + variables: withVariables("EVM"), result: ` { "createFeedsManagerChainConfig": { diff --git a/core/web/resolver/node_test.go b/core/web/resolver/node_test.go index 870f694990f..735c2cb7867 100644 --- a/core/web/resolver/node_test.go +++ b/core/web/resolver/node_test.go @@ -10,6 +10,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/types" chainlinkmocks "github.com/smartcontractkit/chainlink/v2/core/services/chainlink/mocks" + "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/web/testutils" ) @@ -50,8 +51,11 @@ func TestResolver_Nodes(t *testing.T) { State: "alive", }, }, - Relayers: []loop.Relayer{ - testutils.MockRelayer{ChainStatus: types.ChainStatus{ + Relayers: map[types.RelayID]loop.Relayer{ + types.RelayID{ + Network: relay.NetworkEVM, + ChainID: "1", + }: testutils.MockRelayer{ChainStatus: types.ChainStatus{ ID: "1", Enabled: true, Config: "", @@ -124,8 +128,11 @@ func Test_NodeQuery(t *testing.T) { name: "success", authenticated: true, before: func(ctx context.Context, f *gqlTestFramework) { - f.App.On("GetRelayers").Return(&chainlinkmocks.FakeRelayerChainInteroperators{Relayers: []loop.Relayer{ - testutils.MockRelayer{NodeStatuses: []types.NodeStatus{ + f.App.On("GetRelayers").Return(&chainlinkmocks.FakeRelayerChainInteroperators{Relayers: map[types.RelayID]loop.Relayer{ + types.RelayID{ + Network: relay.NetworkEVM, + ChainID: "1", + }: testutils.MockRelayer{NodeStatuses: []types.NodeStatus{ { Name: "node-name", Config: "Name='node-name'\nOrder=11\nHTTPURL='http://some-url'\nWSURL='ws://some-url'", @@ -148,7 +155,7 @@ func Test_NodeQuery(t *testing.T) { name: "not found error", authenticated: true, before: func(ctx context.Context, f *gqlTestFramework) { - f.App.On("GetRelayers").Return(&chainlinkmocks.FakeRelayerChainInteroperators{Relayers: []loop.Relayer{}}) + f.App.On("GetRelayers").Return(&chainlinkmocks.FakeRelayerChainInteroperators{Relayers: map[types.RelayID]loop.Relayer{}}) }, query: query, result: ` diff --git a/core/web/resolver/query.go b/core/web/resolver/query.go index b44ac751db9..d3e1215834a 100644 --- a/core/web/resolver/query.go +++ b/core/web/resolver/query.go @@ -10,13 +10,14 @@ import ( "github.com/graph-gophers/graphql-go" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-common/pkg/types" + commonTypes "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" "github.com/smartcontractkit/chainlink/v2/core/utils/stringutils" + "github.com/smartcontractkit/chainlink/v2/core/web/loader" ) // Bridge retrieves a bridges by name. @@ -68,18 +69,15 @@ func (r *Resolver) Chain(ctx context.Context, args struct{ ID graphql.ID }) (*Ch return nil, err } - cs, _, err := r.App.EVMORM().Chains(string(args.ID)) + id, err := loader.GetChainByID(ctx, string(args.ID)) if err != nil { + if errors.Is(err, chains.ErrNotFound) { + return NewChainPayload(commonTypes.ChainStatusWithID{}, chains.ErrNotFound), nil + } return nil, err } - l := len(cs) - if l == 0 { - return NewChainPayload(types.ChainStatus{}, chains.ErrNotFound), nil - } - if l > 1 { - return nil, fmt.Errorf("multiple chains found: %d", len(cs)) - } - return NewChainPayload(cs[0], nil), nil + + return NewChainPayload(*id, nil), nil } // Chains retrieves a paginated list of chains. @@ -94,16 +92,25 @@ func (r *Resolver) Chains(ctx context.Context, args struct { offset := pageOffset(args.Offset) limit := pageLimit(args.Limit) - var chains []types.ChainStatus - for _, rel := range r.App.GetRelayers().Slice() { - status, err := rel.GetChainStatus(ctx) + var chains []commonTypes.ChainStatusWithID + relayersMap, err := r.App.GetRelayers().GetIDToRelayerMap() + if err != nil { + return nil, err + } + + for k, v := range relayersMap { + s, err := v.GetChainStatus(ctx) if err != nil { return nil, err } - chains = append(chains, status) + + chains = append(chains, commonTypes.ChainStatusWithID{ + ChainStatus: s, + RelayID: k, + }) } - count := len(chains) + count := len(chains) if count == 0 { //No chains are configured, return an empty ChainsPayload, so we don't break the UI return NewChainsPayload(nil, 0), nil @@ -118,9 +125,19 @@ func (r *Resolver) Chains(ctx context.Context, args struct { end = offset + limit } + sortByNetworkAndID(chains) return NewChainsPayload(chains[offset:end], int32(count)), nil } +func sortByNetworkAndID(chains []commonTypes.ChainStatusWithID) { + sort.SliceStable(chains, func(i, j int) bool { + if chains[i].Network == chains[j].Network { + return chains[i].ID < chains[j].ID + } + return chains[i].Network < chains[j].Network + }) +} + // FeedsManager retrieves a feeds manager by id. func (r *Resolver) FeedsManager(ctx context.Context, args struct{ ID graphql.ID }) (*FeedsManagerPayloadResolver, error) { if err := authenticateUser(ctx); err != nil { @@ -565,6 +582,30 @@ func (r *Resolver) AptosKeys(ctx context.Context) (*AptosKeysPayloadResolver, er return NewAptosKeysPayload(keys), nil } +func (r *Resolver) CosmosKeys(ctx context.Context) (*CosmosKeysPayloadResolver, error) { + if err := authenticateUser(ctx); err != nil { + return nil, err + } + keys, err := r.App.GetKeyStore().Cosmos().GetAll() + if err != nil { + return nil, err + } + + return NewCosmosKeysPayload(keys), nil +} + +func (r *Resolver) StarkNetKeys(ctx context.Context) (*StarkNetKeysPayloadResolver, error) { + if err := authenticateUser(ctx); err != nil { + return nil, err + } + keys, err := r.App.GetKeyStore().StarkNet().GetAll() + if err != nil { + return nil, err + } + + return NewStarkNetKeysPayload(keys), nil +} + func (r *Resolver) SQLLogging(ctx context.Context) (*GetSQLLoggingPayloadResolver, error) { if err := authenticateUser(ctx); err != nil { return nil, err diff --git a/core/web/resolver/resolver_test.go b/core/web/resolver/resolver_test.go index a8af6eae7ff..0d365b0891e 100644 --- a/core/web/resolver/resolver_test.go +++ b/core/web/resolver/resolver_test.go @@ -52,6 +52,8 @@ type mocks struct { vrf *keystoreMocks.VRF solana *keystoreMocks.Solana aptos *keystoreMocks.Aptos + cosmos *keystoreMocks.Cosmos + starknet *keystoreMocks.StarkNet chain *legacyEvmORMMocks.Chain legacyEVMChains *legacyEvmORMMocks.LegacyChainContainer relayerChainInterops *chainlinkMocks.FakeRelayerChainInteroperators @@ -108,6 +110,8 @@ func setupFramework(t *testing.T) *gqlTestFramework { vrf: keystoreMocks.NewVRF(t), solana: keystoreMocks.NewSolana(t), aptos: keystoreMocks.NewAptos(t), + cosmos: keystoreMocks.NewCosmos(t), + starknet: keystoreMocks.NewStarkNet(t), chain: legacyEvmORMMocks.NewChain(t), legacyEVMChains: legacyEvmORMMocks.NewLegacyChainContainer(t), relayerChainInterops: &chainlinkMocks.FakeRelayerChainInteroperators{}, diff --git a/core/web/resolver/starknet_key.go b/core/web/resolver/starknet_key.go new file mode 100644 index 00000000000..a3835e5153a --- /dev/null +++ b/core/web/resolver/starknet_key.go @@ -0,0 +1,43 @@ +package resolver + +import ( + "github.com/graph-gophers/graphql-go" + + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/starkkey" +) + +type StarkNetKeyResolver struct { + key starkkey.Key +} + +func NewStarkNetKey(key starkkey.Key) *StarkNetKeyResolver { + return &StarkNetKeyResolver{key: key} +} + +func NewStarkNetKeys(keys []starkkey.Key) []*StarkNetKeyResolver { + var resolvers []*StarkNetKeyResolver + + for _, k := range keys { + resolvers = append(resolvers, NewStarkNetKey(k)) + } + + return resolvers +} + +func (r *StarkNetKeyResolver) ID() graphql.ID { + return graphql.ID(r.key.StarkKeyStr()) +} + +// -- GetStarkNetKeys Query -- + +type StarkNetKeysPayloadResolver struct { + keys []starkkey.Key +} + +func NewStarkNetKeysPayload(keys []starkkey.Key) *StarkNetKeysPayloadResolver { + return &StarkNetKeysPayloadResolver{keys: keys} +} + +func (r *StarkNetKeysPayloadResolver) Results() []*StarkNetKeyResolver { + return NewStarkNetKeys(r.keys) +} diff --git a/core/web/resolver/starknet_key_test.go b/core/web/resolver/starknet_key_test.go new file mode 100644 index 00000000000..dff26afb33f --- /dev/null +++ b/core/web/resolver/starknet_key_test.go @@ -0,0 +1,74 @@ +package resolver + +import ( + "context" + "errors" + "fmt" + "testing" + + gqlerrors "github.com/graph-gophers/graphql-go/errors" + + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/keystest" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/starkkey" +) + +func TestResolver_StarkNetKeys(t *testing.T) { + t.Parallel() + + query := ` + query GetStarkNetKeys { + starknetKeys { + results { + id + } + } + }` + k := starkkey.MustNewInsecure(keystest.NewRandReaderFromSeed(1)) + result := fmt.Sprintf(` + { + "starknetKeys": { + "results": [ + { + "id": "%s" + } + ] + } + }`, k.StarkKeyStr()) + gError := errors.New("error") + + testCases := []GQLTestCase{ + unauthorizedTestCase(GQLTestCase{query: query}, "starknetKeys"), + { + name: "success", + authenticated: true, + before: func(ctx context.Context, f *gqlTestFramework) { + f.Mocks.starknet.On("GetAll").Return([]starkkey.Key{k}, nil) + f.Mocks.keystore.On("StarkNet").Return(f.Mocks.starknet) + f.App.On("GetKeyStore").Return(f.Mocks.keystore) + }, + query: query, + result: result, + }, + { + name: "no keys returned by GetAll", + authenticated: true, + before: func(ctx context.Context, f *gqlTestFramework) { + f.Mocks.starknet.On("GetAll").Return([]starkkey.Key{}, gError) + f.Mocks.keystore.On("StarkNet").Return(f.Mocks.starknet) + f.App.On("GetKeyStore").Return(f.Mocks.keystore) + }, + query: query, + result: `null`, + errors: []*gqlerrors.QueryError{ + { + Extensions: nil, + ResolverError: gError, + Path: []interface{}{"starknetKeys"}, + Message: gError.Error(), + }, + }, + }, + } + + RunGQLTests(t, testCases) +} diff --git a/core/web/schema/schema.graphql b/core/web/schema/schema.graphql index 845570c727f..415be25b77d 100644 --- a/core/web/schema/schema.graphql +++ b/core/web/schema/schema.graphql @@ -34,6 +34,8 @@ type Query { p2pKeys: P2PKeysPayload! solanaKeys: SolanaKeysPayload! aptosKeys: AptosKeysPayload! + cosmosKeys: CosmosKeysPayload! + starknetKeys: StarkNetKeysPayload! sqlLogging: GetSQLLoggingPayload! vrfKey(id: ID!): VRFKeyPayload! vrfKeys: VRFKeysPayload! diff --git a/core/web/schema/type/chain.graphql b/core/web/schema/type/chain.graphql index dcfac1f41d9..1bacaee4a48 100644 --- a/core/web/schema/type/chain.graphql +++ b/core/web/schema/type/chain.graphql @@ -2,6 +2,7 @@ type Chain { id: ID! enabled: Boolean! config: String! + network: String! } union ChainPayload = Chain | NotFoundError diff --git a/core/web/schema/type/cosmos_key.graphql b/core/web/schema/type/cosmos_key.graphql new file mode 100644 index 00000000000..47de4ff9c41 --- /dev/null +++ b/core/web/schema/type/cosmos_key.graphql @@ -0,0 +1,7 @@ +type CosmosKey { + id: ID! +} + +type CosmosKeysPayload { + results: [CosmosKey!]! +} diff --git a/core/web/schema/type/starknet_key.graphql b/core/web/schema/type/starknet_key.graphql new file mode 100644 index 00000000000..bf8dbb864c2 --- /dev/null +++ b/core/web/schema/type/starknet_key.graphql @@ -0,0 +1,7 @@ +type StarkNetKey { + id: ID! +} + +type StarkNetKeysPayload { + results: [StarkNetKey!]! +} diff --git a/flake.lock b/flake.lock index 71af2318c95..77dddea4060 100644 --- a/flake.lock +++ b/flake.lock @@ -40,24 +40,6 @@ "type": "github" } }, - "goreleaser-nur": { - "inputs": { - "nixpkgs": "nixpkgs_2" - }, - "locked": { - "lastModified": 1726594821, - "narHash": "sha256-ORImH+i+zOCMOdztNDqGDbyyFRC/FKmgbX8w50TNbQY=", - "owner": "goreleaser", - "repo": "nur", - "rev": "bd2ee272ddfffbda9377a472131728e83ce2332d", - "type": "github" - }, - "original": { - "owner": "goreleaser", - "repo": "nur", - "type": "github" - } - }, "nixpkgs": { "locked": { "lastModified": 1666753130, @@ -73,22 +55,6 @@ } }, "nixpkgs_2": { - "locked": { - "lastModified": 1624561540, - "narHash": "sha256-izJ2PYZMGMsSkg+e7c9A1x3t/yOLT+qzUM6WQsc2tqo=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "c6a049a3d32293b24c0f894a840872cf67fd7c11", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_3": { "locked": { "lastModified": 1725103162, "narHash": "sha256-Ym04C5+qovuQDYL/rKWSR+WESseQBbNAe5DsXNx5trY=", @@ -104,28 +70,11 @@ "type": "github" } }, - "nur": { - "locked": { - "lastModified": 1727912806, - "narHash": "sha256-LDOTTOGPaEP233gBrL8dnPGopc1lqcJFe0VB/+K/yWc=", - "owner": "nix-community", - "repo": "NUR", - "rev": "9d9bcd30fec126b08b49020b7af08bc1aad66210", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "NUR", - "type": "github" - } - }, "root": { "inputs": { "flake-utils": "flake-utils", "foundry": "foundry", - "goreleaser-nur": "goreleaser-nur", - "nixpkgs": "nixpkgs_3", - "nur": "nur" + "nixpkgs": "nixpkgs_2" } }, "systems": { diff --git a/flake.nix b/flake.nix index 0d1aac2f05d..7e188857ba4 100644 --- a/flake.nix +++ b/flake.nix @@ -6,8 +6,6 @@ foundry.url = "github:shazow/foundry.nix/monthly"; flake-utils.url = "github:numtide/flake-utils"; foundry.inputs.flake-utils.follows = "flake-utils"; - nur.url = "github:nix-community/NUR"; - goreleaser-nur.url = "github:goreleaser/nur"; }; outputs = inputs @ { @@ -15,30 +13,15 @@ nixpkgs, flake-utils, foundry, - nur, - goreleaser-nur, ... }: flake-utils.lib.eachDefaultSystem (system: let - isCrib = builtins.getEnv "IS_CRIB" == "true"; - pkgs = import nixpkgs { inherit system; - config = { allowUnfree = true; }; - overlays = [ - (final: prev: { - nur = import nur - { - pkgs = prev; - repoOverrides = { - goreleaser = import goreleaser-nur { pkgs = prev; }; - }; - }; - }) - foundry.overlay - ]; - }; + pkgs = import nixpkgs { + inherit system; + overlays = [foundry.overlay]; + }; in rec { devShell = pkgs.callPackage ./shell.nix { - isCrib = isCrib; inherit pkgs; }; formatter = pkgs.nixpkgs-fmt; diff --git a/go.md b/go.md index 3100bd2db0e..d9d4ea5cf35 100644 --- a/go.md +++ b/go.md @@ -39,6 +39,8 @@ flowchart LR click chainlink-data-streams href "https://github.com/smartcontractkit/chainlink-data-streams" chainlink/v2 --> chainlink-feeds click chainlink-feeds href "https://github.com/smartcontractkit/chainlink-feeds" + chainlink/v2 --> chainlink-protos/orchestrator + click chainlink-protos/orchestrator href "https://github.com/smartcontractkit/chainlink-protos" chainlink/v2 --> chainlink-solana click chainlink-solana href "https://github.com/smartcontractkit/chainlink-solana" chainlink/v2 --> chainlink-starknet/relayer @@ -69,6 +71,7 @@ flowchart LR chainlink-feeds --> chainlink-common chainlink-feeds --> libocr chainlink-feeds --> grpc-proxy + chainlink-protos/orchestrator --> wsrpc chainlink-solana --> chainlink-common chainlink-solana --> libocr chainlink-solana --> grpc-proxy diff --git a/go.mod b/go.mod index 24c841fa59e..7ad7cba1809 100644 --- a/go.mod +++ b/go.mod @@ -73,13 +73,14 @@ require ( github.com/scylladb/go-reflectx v1.0.1 github.com/shirou/gopsutil/v3 v3.24.3 github.com/shopspring/decimal v1.4.0 - github.com/smartcontractkit/chain-selectors v1.0.23 + github.com/smartcontractkit/chain-selectors v1.0.27 github.com/smartcontractkit/chainlink-automation v0.8.0 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20241010120731-ae3e8f4935a0 - github.com/smartcontractkit/chainlink-common v0.3.0 + github.com/smartcontractkit/chainlink-ccip v0.0.0-20241014104242-9227e5c976a7 + github.com/smartcontractkit/chainlink-common v0.3.1-0.20241014122810-6c3cc4d0dc87 github.com/smartcontractkit/chainlink-cosmos v0.5.1 github.com/smartcontractkit/chainlink-data-streams v0.1.0 github.com/smartcontractkit/chainlink-feeds v0.1.1 + github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241010140936-4e1d0ae8315a github.com/smartcontractkit/chainlink-starknet/relayer v0.1.0 github.com/smartcontractkit/libocr v0.0.0-20241007185508-adbe57025f12 @@ -115,7 +116,7 @@ require ( golang.org/x/tools v0.25.0 gonum.org/v1/gonum v0.15.0 google.golang.org/grpc v1.65.0 - google.golang.org/protobuf v1.34.2 + google.golang.org/protobuf v1.35.1 gopkg.in/guregu/null.v4 v4.0.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 diff --git a/go.sum b/go.sum index cc8ac4b8687..c9a77eca8de 100644 --- a/go.sum +++ b/go.sum @@ -1051,20 +1051,22 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartcontractkit/chain-selectors v1.0.23 h1:D2Eaex4Cw/O7Lg3tX6WklOqnjjIQAEBnutCtksPzVDY= -github.com/smartcontractkit/chain-selectors v1.0.23/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= +github.com/smartcontractkit/chain-selectors v1.0.27 h1:VE/ftX9Aae4gnw67yR1raKi+30iWKL/sWq8uyiLHM8k= +github.com/smartcontractkit/chain-selectors v1.0.27/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v0.8.0 h1:hFz2EHU06bkEfhcqhK8JdjKTWpDOr0XJ6xL9oELDoUg= github.com/smartcontractkit/chainlink-automation v0.8.0/go.mod h1:ObdjDfgGIaiE48Bb3yYcx1CeGBm392WlEw92U83LlUA= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241010120731-ae3e8f4935a0 h1:nKfjG9fufMQspKO0hFGK2B9ICL+tgajwP+nAXYP0LPc= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241010120731-ae3e8f4935a0/go.mod h1:DjyfH0AGcqctKB6DaRUpRL6GJ2SbrpiaBkA4h24/oMY= -github.com/smartcontractkit/chainlink-common v0.3.0 h1:mUXHBzzw2qPKyw6gPAC8JhO+ryT8maY+rBi9NFtqEy0= -github.com/smartcontractkit/chainlink-common v0.3.0/go.mod h1:tsGgeEJc5SUSlfVGSX0wR0EkRU3pM58D6SKF97V68ko= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241014104242-9227e5c976a7 h1:aMG3BllvgeL/vsqkebuAhWoIWOnitKnN1VxibdzGnYo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241014104242-9227e5c976a7/go.mod h1:H4BTXnZBhwRdsAFjqWZpB1/f3IZnuB/Ql7pXPmokzXg= +github.com/smartcontractkit/chainlink-common v0.3.1-0.20241014122810-6c3cc4d0dc87 h1:48qauRZcdxAOrgeko4RTm9ti4GGbSfzkcI4Dr/1FmjU= +github.com/smartcontractkit/chainlink-common v0.3.1-0.20241014122810-6c3cc4d0dc87/go.mod h1:tsGgeEJc5SUSlfVGSX0wR0EkRU3pM58D6SKF97V68ko= github.com/smartcontractkit/chainlink-cosmos v0.5.1 h1:2xeZWh+4/w7xalTdAu8jqgFuxZ291aYTEwZhlQEv/BY= github.com/smartcontractkit/chainlink-cosmos v0.5.1/go.mod h1:c1wUtVxXUqW4PzuCQhuHaBDZFv9XAQjhKTqam7GLGIU= github.com/smartcontractkit/chainlink-data-streams v0.1.0 h1:wcRJRm7eqfbgN+Na+GjAe0/IUn6XwmSagFHqIWHHBGk= github.com/smartcontractkit/chainlink-data-streams v0.1.0/go.mod h1:lmdRVjg49Do+5tkk9V5iAhi+Jm2kXhjZXWAbzh7xg7o= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4= +github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 h1:PBUaFfPLm+Efq7H9kdfGBivH+QhJ6vB5EZTR/sCZsxI= +github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0/go.mod h1:m/A3lqD7ms/RsQ9BT5P2uceYY0QX5mIt4KQxT2G6qEo= github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241010140936-4e1d0ae8315a h1:WdteRQ8p+4m9VPA5ibwheQBeBd1ndy1YlE6y0K/qeVE= github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241010140936-4e1d0ae8315a/go.mod h1:XDrfLscHNHXIrB8MJVEYRcCVxxxO4BflcS+S6rlcgU4= github.com/smartcontractkit/chainlink-starknet/relayer v0.1.0 h1:C00zDQ6AQdR9JFrHnOBEhC2TlYVzVSsC7k5AZ7hXwHI= @@ -1679,8 +1681,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/integration-tests/deployment/address_book.go b/integration-tests/deployment/address_book.go index e0efbc00af7..8385bc0e9f1 100644 --- a/integration-tests/deployment/address_book.go +++ b/integration-tests/deployment/address_book.go @@ -163,3 +163,19 @@ func NewMemoryAddressBookFromMap(addressesByChain map[uint64]map[string]TypeAndV AddressesByChain: addressesByChain, } } + +// SearchAddressBook search an address book for a given chain and contract type and return the first matching address. +func SearchAddressBook(ab AddressBook, chain uint64, typ ContractType) (string, error) { + addrs, err := ab.AddressesForChain(chain) + if err != nil { + return "", err + } + + for addr, tv := range addrs { + if tv.Type == typ { + return addr, nil + } + } + + return "", fmt.Errorf("not found") +} diff --git a/integration-tests/deployment/ccip/add_chain.go b/integration-tests/deployment/ccip/add_chain.go index 1816e55d36e..bd72dcb9576 100644 --- a/integration-tests/deployment/ccip/add_chain.go +++ b/integration-tests/deployment/ccip/add_chain.go @@ -30,6 +30,7 @@ func NewChainInboundProposal( newChainSel uint64, sources []uint64, tokenConfig TokenConfig, + rmnHomeAddress []byte, ) (*timelock.MCMSWithTimelockProposal, error) { // Generate proposal which enables new destination (from test router) on all source chains. var batches []timelock.BatchChainOperation @@ -135,6 +136,7 @@ func NewChainInboundProposal( feedChainSel, tokenConfig.GetTokenInfo(e.Logger, state.Chains[newChainSel].LinkToken), nodes.NonBootstraps(), + rmnHomeAddress, ) if err != nil { return nil, err diff --git a/integration-tests/deployment/ccip/add_chain_test.go b/integration-tests/deployment/ccip/add_chain_test.go index 00147c2b474..cb48a62915a 100644 --- a/integration-tests/deployment/ccip/add_chain_test.go +++ b/integration-tests/deployment/ccip/add_chain_test.go @@ -6,6 +6,8 @@ import ( "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_home" + "github.com/smartcontractkit/chainlink-ccip/pluginconfig" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" @@ -66,8 +68,14 @@ func TestAddChainInbound(t *testing.T) { } } + rmnHomeAddress, err := deployment.SearchAddressBook(e.Ab, e.HomeChainSel, RMNHome) + require.NoError(t, err) + require.True(t, common.IsHexAddress(rmnHomeAddress)) + rmnHome, err := rmn_home.NewRMNHome(common.HexToAddress(rmnHomeAddress), e.Env.Chains[e.HomeChainSel].Client) + require.NoError(t, err) + // Deploy contracts to new chain - err = DeployChainContracts(e.Env, e.Env.Chains[newChain], e.Ab, e.FeeTokenContracts[newChain], NewTestMCMSConfig(t, e.Env)) + err = DeployChainContracts(e.Env, e.Env.Chains[newChain], e.Ab, e.FeeTokenContracts[newChain], NewTestMCMSConfig(t, e.Env), rmnHome) require.NoError(t, err) state, err = LoadOnchainState(e.Env, e.Ab) require.NoError(t, err) @@ -122,7 +130,8 @@ func TestAddChainInbound(t *testing.T) { require.Equal(t, state.Chains[e.HomeChainSel].Timelock.Address(), crOwner) // Generate and sign inbound proposal to new 4th chain. - chainInboundProposal, err := NewChainInboundProposal(e.Env, state, e.HomeChainSel, e.FeedChainSel, newChain, initialDeploy, tokenConfig) + rmnHomeAddressBytes := common.HexToAddress(rmnHomeAddress).Bytes() + chainInboundProposal, err := NewChainInboundProposal(e.Env, state, e.HomeChainSel, e.FeedChainSel, newChain, initialDeploy, tokenConfig, rmnHomeAddressBytes) require.NoError(t, err) chainInboundExec := SignProposal(t, e.Env, chainInboundProposal) for _, sel := range initialDeploy { diff --git a/integration-tests/deployment/ccip/changeset/initial_deploy.go b/integration-tests/deployment/ccip/changeset/initial_deploy.go index d970086d155..52406487b97 100644 --- a/integration-tests/deployment/ccip/changeset/initial_deploy.go +++ b/integration-tests/deployment/ccip/changeset/initial_deploy.go @@ -12,8 +12,7 @@ import ( // TODO: Maybe there's a generics approach here? // Note if the change set is a deployment and it fails we have 2 options: // - Just throw away the addresses, fix issue and try again (potentially expensive on mainnet) -func InitialDeployChangeSet(env deployment.Environment, c ccipdeployment.DeployCCIPContractConfig) (deployment.ChangesetOutput, error) { - ab := deployment.NewMemoryAddressBook() +func InitialDeployChangeSet(ab deployment.AddressBook, env deployment.Environment, c ccipdeployment.DeployCCIPContractConfig) (deployment.ChangesetOutput, error) { err := ccipdeployment.DeployCCIPContracts(env, ab, c) if err != nil { env.Logger.Errorw("Failed to deploy CCIP contracts", "err", err, "addresses", ab) diff --git a/integration-tests/deployment/ccip/changeset/initial_deploy_test.go b/integration-tests/deployment/ccip/changeset/initial_deploy_test.go index ee207391918..ca410517d9b 100644 --- a/integration-tests/deployment/ccip/changeset/initial_deploy_test.go +++ b/integration-tests/deployment/ccip/changeset/initial_deploy_test.go @@ -37,7 +37,7 @@ func TestInitialDeploy(t *testing.T) { }, ) - output, err := InitialDeployChangeSet(tenv.Env, ccdeploy.DeployCCIPContractConfig{ + output, err := InitialDeployChangeSet(tenv.Ab, tenv.Env, ccdeploy.DeployCCIPContractConfig{ HomeChainSel: tenv.HomeChainSel, FeedChainSel: tenv.FeedChainSel, ChainsToDeploy: tenv.Env.AllChainSelectors(), @@ -47,7 +47,6 @@ func TestInitialDeploy(t *testing.T) { FeeTokenContracts: tenv.FeeTokenContracts, }) require.NoError(t, err) - require.NoError(t, tenv.Ab.Merge(output.AddressBook)) // Get new state after migration. state, err = ccdeploy.LoadOnchainState(e, tenv.Ab) require.NoError(t, err) diff --git a/integration-tests/deployment/ccip/deploy.go b/integration-tests/deployment/ccip/deploy.go index d6b071d807b..309a47b7b31 100644 --- a/integration-tests/deployment/ccip/deploy.go +++ b/integration-tests/deployment/ccip/deploy.go @@ -172,6 +172,20 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c return err } + rmnHomeAddress, err := deployment.SearchAddressBook(ab, c.HomeChainSel, RMNHome) + if err != nil { + return fmt.Errorf("rmn home address not found: %w", err) + } + if !common.IsHexAddress(rmnHomeAddress) { + return fmt.Errorf("rmn home address %s is not a valid address", rmnHomeAddress) + } + + rmnHome, err := rmn_home.NewRMNHome(common.HexToAddress(rmnHomeAddress), e.Chains[c.HomeChainSel].Client) + if err != nil { + e.Logger.Errorw("Failed to get rmn home", "err", err) + return err + } + for _, chainSel := range c.ChainsToDeploy { chain, ok := e.Chains[chainSel] if !ok { @@ -181,7 +195,7 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c if !ok { return fmt.Errorf("chain %d config not found", chainSel) } - err = DeployChainContracts(e, chain, ab, chainConfig, c.MCMSConfig) + err = DeployChainContracts(e, chain, ab, chainConfig, c.MCMSConfig, rmnHome) if err != nil { return err } @@ -214,6 +228,7 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c e.Logger, capReg, ccipHome, + common.HexToAddress(rmnHomeAddress).Bytes(), chainState.OffRamp, c.FeedChainSel, tokenInfo, @@ -403,6 +418,7 @@ func DeployChainContracts( ab deployment.AddressBook, contractConfig FeeTokenContracts, mcmsConfig MCMSConfig, + rmnHome *rmn_home.RMNHome, ) error { mcmsContracts, err := DeployMCMSContracts(e.Logger, chain, ab, mcmsConfig) if err != nil { @@ -443,9 +459,15 @@ func DeployChainContracts( } e.Logger.Infow("deployed RMNRemote", "addr", rmnRemote.Address) - // TODO: Correctly configure RMN remote with config digest from RMN home. + activeDigest, err := rmnHome.GetActiveDigest(&bind.CallOpts{}) + if err != nil { + e.Logger.Errorw("Failed to get active digest", "err", err) + return err + } + e.Logger.Infow("setting active home digest to rmn remote", "digest", activeDigest) + tx, err := rmnRemote.Contract.SetConfig(chain.DeployerKey, rmn_remote.RMNRemoteConfig{ - RmnHomeContractConfigDigest: [32]byte{1}, + RmnHomeContractConfigDigest: activeDigest, Signers: []rmn_remote.RMNRemoteSigner{}, MinSigners: 0, // TODO: update when we have signers }) diff --git a/integration-tests/deployment/ccip/deploy_home_chain.go b/integration-tests/deployment/ccip/deploy_home_chain.go index fe889348478..316f8604fd5 100644 --- a/integration-tests/deployment/ccip/deploy_home_chain.go +++ b/integration-tests/deployment/ccip/deploy_home_chain.go @@ -3,7 +3,6 @@ package ccipdeployment import ( "bytes" "context" - "crypto/rand" "encoding/hex" "fmt" "time" @@ -158,6 +157,7 @@ func DeployCapReg(lggr logger.Logger, ab deployment.AddressBook, chain deploymen lggr.Errorw("Failed to get RMNHome active digest", "err", err) return nil, err } + lggr.Infow("Got rmn home active digest", "digest", rmnActiveDigest) if rmnActiveDigest != rmnCandidateDigest { lggr.Errorw("RMNHome active digest does not match previously candidate digest", @@ -268,6 +268,7 @@ func BuildAddDONArgs( // Token address on Dest chain to aggregate address on feed chain tokenInfo map[ocrtypes.Account]pluginconfig.TokenInfo, nodes deployment.Nodes, + rmnHomeAddress []byte, ) (map[cctypes.PluginType]ccip_home.CCIPHomeOCR3Config, error) { p2pIDs := nodes.PeerIDs() // Get OCR3 Config from helper @@ -375,23 +376,13 @@ func BuildAddDONArgs( OfframpAddress: offRamp.Address().Bytes(), Nodes: ocrNodes, OffchainConfig: offchainConfig, - // TODO: Deploy RMNHome and set address here - RmnHomeAddress: common.BytesToAddress(randomBytes(20)).Bytes(), + RmnHomeAddress: rmnHomeAddress, } } return ocr3Configs, nil } -func randomBytes(n int) []byte { - b := make([]byte, n) - _, err := rand.Read(b) - if err != nil { - panic(err) - } - return b -} - func LatestCCIPDON(registry *capabilities_registry.CapabilitiesRegistry) (*capabilities_registry.CapabilitiesRegistryDONInfo, error) { dons, err := registry.GetDONs(nil) if err != nil { @@ -763,6 +754,7 @@ func AddDON( lggr logger.Logger, capReg *capabilities_registry.CapabilitiesRegistry, ccipHome *ccip_home.CCIPHome, + rmnHomeAddress []byte, offRamp *offramp.OffRamp, feedChainSel uint64, // Token address on Dest chain to aggregate address on feed chain @@ -771,7 +763,7 @@ func AddDON( home deployment.Chain, nodes deployment.Nodes, ) error { - ocrConfigs, err := BuildAddDONArgs(lggr, offRamp, dest, feedChainSel, tokenInfo, nodes) + ocrConfigs, err := BuildAddDONArgs(lggr, offRamp, dest, feedChainSel, tokenInfo, nodes, rmnHomeAddress) if err != nil { return err } diff --git a/integration-tests/deployment/keystone/deploy.go b/integration-tests/deployment/keystone/deploy.go index 67613a67919..204247fd721 100644 --- a/integration-tests/deployment/keystone/deploy.go +++ b/integration-tests/deployment/keystone/deploy.go @@ -577,7 +577,7 @@ func registerNodes(lggr logger.Logger, req *registerNodesRequest) (*registerNode NodeOperatorId: nop.NodeOperatorId, Signer: n.Signer, P2pId: n.P2PKey, - EncryptionPublicKey: [32]byte{0x01}, // unused in tests + EncryptionPublicKey: n.EncryptionPublicKey, HashedCapabilityIds: hashedCapabilityIds, } } else { diff --git a/integration-tests/deployment/keystone/ocr3config.go b/integration-tests/deployment/keystone/ocr3config.go index 721e85e8cd5..343ddae696f 100644 --- a/integration-tests/deployment/keystone/ocr3config.go +++ b/integration-tests/deployment/keystone/ocr3config.go @@ -61,6 +61,7 @@ type NodeKeys struct { OCR2OffchainPublicKey string `json:"OCR2OffchainPublicKey"` // ocr2off_evm_ OCR2ConfigPublicKey string `json:"OCR2ConfigPublicKey"` // ocr2cfg_evm_ CSAPublicKey string `json:"CSAPublicKey"` + EncryptionPublicKey string `json:"EncryptionPublicKey"` } type Orc2drOracleConfig struct { diff --git a/integration-tests/deployment/keystone/types.go b/integration-tests/deployment/keystone/types.go index dc0ccea75f8..d49d2180436 100644 --- a/integration-tests/deployment/keystone/types.go +++ b/integration-tests/deployment/keystone/types.go @@ -55,13 +55,15 @@ type Nop struct { // with the capabilities registry. Signer and P2PKey are chain agnostic. // TODO: KS-466 when we migrate fully to the JD offchain client, we should be able remove this shim and use environment.Node directly type ocr2Node struct { - ID string - Signer [32]byte // note that in capabilities registry we need a [32]byte, but in the forwarder we need a common.Address [20]byte - P2PKey p2pkey.PeerID - IsBoostrap bool + ID string + Signer [32]byte // note that in capabilities registry we need a [32]byte, but in the forwarder we need a common.Address [20]byte + P2PKey p2pkey.PeerID + EncryptionPublicKey [32]byte + IsBoostrap bool // useful when have to register the ocr3 contract config p2pKeyBundle *v1.OCR2Config_P2PKeyBundle ocrKeyBundle *v1.OCR2Config_OCRKeyBundle + csaKey string // *v1.Node.PublicKey accountAddress string } @@ -77,14 +79,32 @@ func (o *ocr2Node) toNodeKeys() NodeKeys { OCR2OnchainPublicKey: o.ocrKeyBundle.OnchainSigningAddress, OCR2OffchainPublicKey: o.ocrKeyBundle.OffchainPublicKey, OCR2ConfigPublicKey: o.ocrKeyBundle.ConfigPublicKey, + CSAPublicKey: o.csaKey, + // default value of encryption public key is the CSA public key + // TODO: DEVSVCS-760 + EncryptionPublicKey: o.csaKey, // TODO Aptos support. How will that be modeled in clo data? } } -func newOcr2Node(id string, ccfg *v1.ChainConfig) (*ocr2Node, error) { +func newOcr2Node(id string, ccfg *v1.ChainConfig, csaPubKey string) (*ocr2Node, error) { if ccfg == nil { return nil, errors.New("nil ocr2config") } + if csaPubKey == "" { + return nil, errors.New("empty csa public key") + } + // parse csapublic key to + csaKey, err := hex.DecodeString(csaPubKey) + if err != nil { + return nil, fmt.Errorf("failed to decode csa public key %s: %w", csaPubKey, err) + } + if len(csaKey) != 32 { + return nil, fmt.Errorf("invalid csa public key '%s'. expected len 32 got %d", csaPubKey, len(csaKey)) + } + var csaKeyb [32]byte + copy(csaKeyb[:], csaKey) + ocfg := ccfg.Ocr2Config p := p2pkey.PeerID{} if err := p.UnmarshalString(ocfg.P2PKeyBundle.PeerId); err != nil { @@ -104,13 +124,15 @@ func newOcr2Node(id string, ccfg *v1.ChainConfig) (*ocr2Node, error) { copy(sigb[:], signerB) return &ocr2Node{ - ID: id, - Signer: sigb, - P2PKey: p, - IsBoostrap: ocfg.IsBootstrap, - p2pKeyBundle: ocfg.P2PKeyBundle, - ocrKeyBundle: ocfg.OcrKeyBundle, - accountAddress: ccfg.AccountAddress, + ID: id, + Signer: sigb, + P2PKey: p, + EncryptionPublicKey: csaKeyb, + IsBoostrap: ocfg.IsBootstrap, + p2pKeyBundle: ocfg.P2PKeyBundle, + ocrKeyBundle: ocfg.OcrKeyBundle, + accountAddress: ccfg.AccountAddress, + csaKey: csaPubKey, }, nil } @@ -196,13 +218,17 @@ func mapDonsToNodes(dons []DonCapabilities, excludeBootstraps bool) (map[string] for _, don := range dons { for _, nop := range don.Nops { for _, node := range nop.Nodes { + csaPubKey := node.PublicKey + if csaPubKey == nil { + return nil, fmt.Errorf("no public key for node %s", node.ID) + } // the chain configs are equivalent as far as the ocr2 config is concerned so take the first one if len(node.ChainConfigs) == 0 { return nil, fmt.Errorf("no chain configs for node %s. cannot obtain keys", node.ID) } chain := node.ChainConfigs[0] ccfg := chainConfigFromClo(chain) - ocr2n, err := newOcr2Node(node.ID, ccfg) + ocr2n, err := newOcr2Node(node.ID, ccfg, *csaPubKey) if err != nil { return nil, fmt.Errorf("failed to create ocr2 node for node %s: %w", node.ID, err) } diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 303294bb929..3fd83398ead 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -37,10 +37,10 @@ require ( github.com/shopspring/decimal v1.4.0 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86 - github.com/smartcontractkit/chain-selectors v1.0.23 + github.com/smartcontractkit/chain-selectors v1.0.27 github.com/smartcontractkit/chainlink-automation v0.8.0 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20241010120731-ae3e8f4935a0 - github.com/smartcontractkit/chainlink-common v0.3.0 + github.com/smartcontractkit/chainlink-ccip v0.0.0-20241014104242-9227e5c976a7 + github.com/smartcontractkit/chainlink-common v0.3.1-0.20241014122810-6c3cc4d0dc87 github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.0 github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.11-0.20241011153842-b2804aed25b4 github.com/smartcontractkit/chainlink-testing-framework/lib/grafana v1.50.0 @@ -61,8 +61,8 @@ require ( golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 golang.org/x/sync v0.8.0 golang.org/x/text v0.18.0 - google.golang.org/grpc v1.65.0 - google.golang.org/protobuf v1.34.2 + google.golang.org/grpc v1.66.2 + google.golang.org/protobuf v1.35.1 gopkg.in/guregu/null.v4 v4.0.0 gotest.tools/v3 v3.5.1 k8s.io/apimachinery v0.31.0 @@ -410,6 +410,7 @@ require ( github.com/smartcontractkit/chainlink-cosmos v0.5.1 // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.0 // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect + github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 // indirect github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241010140936-4e1d0ae8315a // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.1.0 // indirect github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 8e868f64e8d..4be605913fb 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -480,8 +480,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI= -github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= +github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155 h1:IgJPqnrlY2Mr4pYB6oaMKvFvwJ9H+X6CCY5x1vCTcpc= +github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155/go.mod h1:5Wkq+JduFtdAXihLmeTJf+tRYIT4KBc2vPXDhwVo1pA= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= @@ -1267,6 +1267,8 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= @@ -1396,20 +1398,22 @@ github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86 h1:qQH6fZZe31nBAG6INHph3z5ysDTPptyu0TR9uoJ1+ok= github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86/go.mod h1:WtWOoVQQEHxRHL2hNmuRrvDfYfQG/CioFNoa9Rr2mBE= -github.com/smartcontractkit/chain-selectors v1.0.23 h1:D2Eaex4Cw/O7Lg3tX6WklOqnjjIQAEBnutCtksPzVDY= -github.com/smartcontractkit/chain-selectors v1.0.23/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= +github.com/smartcontractkit/chain-selectors v1.0.27 h1:VE/ftX9Aae4gnw67yR1raKi+30iWKL/sWq8uyiLHM8k= +github.com/smartcontractkit/chain-selectors v1.0.27/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v0.8.0 h1:hFz2EHU06bkEfhcqhK8JdjKTWpDOr0XJ6xL9oELDoUg= github.com/smartcontractkit/chainlink-automation v0.8.0/go.mod h1:ObdjDfgGIaiE48Bb3yYcx1CeGBm392WlEw92U83LlUA= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241010120731-ae3e8f4935a0 h1:nKfjG9fufMQspKO0hFGK2B9ICL+tgajwP+nAXYP0LPc= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241010120731-ae3e8f4935a0/go.mod h1:DjyfH0AGcqctKB6DaRUpRL6GJ2SbrpiaBkA4h24/oMY= -github.com/smartcontractkit/chainlink-common v0.3.0 h1:mUXHBzzw2qPKyw6gPAC8JhO+ryT8maY+rBi9NFtqEy0= -github.com/smartcontractkit/chainlink-common v0.3.0/go.mod h1:tsGgeEJc5SUSlfVGSX0wR0EkRU3pM58D6SKF97V68ko= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241014104242-9227e5c976a7 h1:aMG3BllvgeL/vsqkebuAhWoIWOnitKnN1VxibdzGnYo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241014104242-9227e5c976a7/go.mod h1:H4BTXnZBhwRdsAFjqWZpB1/f3IZnuB/Ql7pXPmokzXg= +github.com/smartcontractkit/chainlink-common v0.3.1-0.20241014122810-6c3cc4d0dc87 h1:48qauRZcdxAOrgeko4RTm9ti4GGbSfzkcI4Dr/1FmjU= +github.com/smartcontractkit/chainlink-common v0.3.1-0.20241014122810-6c3cc4d0dc87/go.mod h1:tsGgeEJc5SUSlfVGSX0wR0EkRU3pM58D6SKF97V68ko= github.com/smartcontractkit/chainlink-cosmos v0.5.1 h1:2xeZWh+4/w7xalTdAu8jqgFuxZ291aYTEwZhlQEv/BY= github.com/smartcontractkit/chainlink-cosmos v0.5.1/go.mod h1:c1wUtVxXUqW4PzuCQhuHaBDZFv9XAQjhKTqam7GLGIU= github.com/smartcontractkit/chainlink-data-streams v0.1.0 h1:wcRJRm7eqfbgN+Na+GjAe0/IUn6XwmSagFHqIWHHBGk= github.com/smartcontractkit/chainlink-data-streams v0.1.0/go.mod h1:lmdRVjg49Do+5tkk9V5iAhi+Jm2kXhjZXWAbzh7xg7o= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4= +github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 h1:PBUaFfPLm+Efq7H9kdfGBivH+QhJ6vB5EZTR/sCZsxI= +github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0/go.mod h1:m/A3lqD7ms/RsQ9BT5P2uceYY0QX5mIt4KQxT2G6qEo= github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241010140936-4e1d0ae8315a h1:WdteRQ8p+4m9VPA5ibwheQBeBd1ndy1YlE6y0K/qeVE= github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241010140936-4e1d0ae8315a/go.mod h1:XDrfLscHNHXIrB8MJVEYRcCVxxxO4BflcS+S6rlcgU4= github.com/smartcontractkit/chainlink-starknet/relayer v0.1.0 h1:C00zDQ6AQdR9JFrHnOBEhC2TlYVzVSsC7k5AZ7hXwHI= @@ -2130,8 +2134,8 @@ google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo= +google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -2144,8 +2148,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index ee4a10afb68..f411341671d 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -15,7 +15,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.33.0 github.com/slack-go/slack v0.12.2 - github.com/smartcontractkit/chainlink-common v0.3.0 + github.com/smartcontractkit/chainlink-common v0.3.1-0.20241014122810-6c3cc4d0dc87 github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.11-0.20241011153842-b2804aed25b4 github.com/smartcontractkit/chainlink-testing-framework/seth v1.50.1 github.com/smartcontractkit/chainlink-testing-framework/wasp v1.50.0 @@ -384,12 +384,13 @@ require ( github.com/shoenig/test v0.6.6 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/smartcontractkit/chain-selectors v1.0.23 // indirect + github.com/smartcontractkit/chain-selectors v1.0.27 // indirect github.com/smartcontractkit/chainlink-automation v0.8.0 // indirect - github.com/smartcontractkit/chainlink-ccip v0.0.0-20241010120731-ae3e8f4935a0 // indirect + github.com/smartcontractkit/chainlink-ccip v0.0.0-20241014104242-9227e5c976a7 // indirect github.com/smartcontractkit/chainlink-cosmos v0.5.1 // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.0 // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect + github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 // indirect github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241010140936-4e1d0ae8315a // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.1.0 // indirect github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.0 // indirect @@ -489,8 +490,8 @@ require ( google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect - google.golang.org/grpc v1.65.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + google.golang.org/grpc v1.66.2 // indirect + google.golang.org/protobuf v1.35.1 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/guregu/null.v4 v4.0.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index d5e0cb739c5..08971a251d6 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -462,8 +462,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI= -github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= +github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155 h1:IgJPqnrlY2Mr4pYB6oaMKvFvwJ9H+X6CCY5x1vCTcpc= +github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155/go.mod h1:5Wkq+JduFtdAXihLmeTJf+tRYIT4KBc2vPXDhwVo1pA= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= @@ -1245,6 +1245,8 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= @@ -1373,20 +1375,22 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ= github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= -github.com/smartcontractkit/chain-selectors v1.0.23 h1:D2Eaex4Cw/O7Lg3tX6WklOqnjjIQAEBnutCtksPzVDY= -github.com/smartcontractkit/chain-selectors v1.0.23/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= +github.com/smartcontractkit/chain-selectors v1.0.27 h1:VE/ftX9Aae4gnw67yR1raKi+30iWKL/sWq8uyiLHM8k= +github.com/smartcontractkit/chain-selectors v1.0.27/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v0.8.0 h1:hFz2EHU06bkEfhcqhK8JdjKTWpDOr0XJ6xL9oELDoUg= github.com/smartcontractkit/chainlink-automation v0.8.0/go.mod h1:ObdjDfgGIaiE48Bb3yYcx1CeGBm392WlEw92U83LlUA= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241010120731-ae3e8f4935a0 h1:nKfjG9fufMQspKO0hFGK2B9ICL+tgajwP+nAXYP0LPc= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241010120731-ae3e8f4935a0/go.mod h1:DjyfH0AGcqctKB6DaRUpRL6GJ2SbrpiaBkA4h24/oMY= -github.com/smartcontractkit/chainlink-common v0.3.0 h1:mUXHBzzw2qPKyw6gPAC8JhO+ryT8maY+rBi9NFtqEy0= -github.com/smartcontractkit/chainlink-common v0.3.0/go.mod h1:tsGgeEJc5SUSlfVGSX0wR0EkRU3pM58D6SKF97V68ko= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241014104242-9227e5c976a7 h1:aMG3BllvgeL/vsqkebuAhWoIWOnitKnN1VxibdzGnYo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241014104242-9227e5c976a7/go.mod h1:H4BTXnZBhwRdsAFjqWZpB1/f3IZnuB/Ql7pXPmokzXg= +github.com/smartcontractkit/chainlink-common v0.3.1-0.20241014122810-6c3cc4d0dc87 h1:48qauRZcdxAOrgeko4RTm9ti4GGbSfzkcI4Dr/1FmjU= +github.com/smartcontractkit/chainlink-common v0.3.1-0.20241014122810-6c3cc4d0dc87/go.mod h1:tsGgeEJc5SUSlfVGSX0wR0EkRU3pM58D6SKF97V68ko= github.com/smartcontractkit/chainlink-cosmos v0.5.1 h1:2xeZWh+4/w7xalTdAu8jqgFuxZ291aYTEwZhlQEv/BY= github.com/smartcontractkit/chainlink-cosmos v0.5.1/go.mod h1:c1wUtVxXUqW4PzuCQhuHaBDZFv9XAQjhKTqam7GLGIU= github.com/smartcontractkit/chainlink-data-streams v0.1.0 h1:wcRJRm7eqfbgN+Na+GjAe0/IUn6XwmSagFHqIWHHBGk= github.com/smartcontractkit/chainlink-data-streams v0.1.0/go.mod h1:lmdRVjg49Do+5tkk9V5iAhi+Jm2kXhjZXWAbzh7xg7o= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4= +github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 h1:PBUaFfPLm+Efq7H9kdfGBivH+QhJ6vB5EZTR/sCZsxI= +github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0/go.mod h1:m/A3lqD7ms/RsQ9BT5P2uceYY0QX5mIt4KQxT2G6qEo= github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241010140936-4e1d0ae8315a h1:WdteRQ8p+4m9VPA5ibwheQBeBd1ndy1YlE6y0K/qeVE= github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241010140936-4e1d0ae8315a/go.mod h1:XDrfLscHNHXIrB8MJVEYRcCVxxxO4BflcS+S6rlcgU4= github.com/smartcontractkit/chainlink-starknet/relayer v0.1.0 h1:C00zDQ6AQdR9JFrHnOBEhC2TlYVzVSsC7k5AZ7hXwHI= @@ -2105,8 +2109,8 @@ google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo= +google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -2119,8 +2123,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/integration-tests/smoke/ccip_test.go b/integration-tests/smoke/ccip_test.go index c4fd42a1bec..ad4c476087c 100644 --- a/integration-tests/smoke/ccip_test.go +++ b/integration-tests/smoke/ccip_test.go @@ -5,9 +5,10 @@ import ( "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-ccip/pluginconfig" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" + "github.com/smartcontractkit/chainlink-ccip/pluginconfig" + "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" ccdeploy "github.com/smartcontractkit/chainlink/integration-tests/deployment/ccip" @@ -35,7 +36,7 @@ func TestInitialDeployOnLocal(t *testing.T) { }, ) // Apply migration - output, err := changeset.InitialDeployChangeSet(tenv.Env, ccdeploy.DeployCCIPContractConfig{ + output, err := changeset.InitialDeployChangeSet(tenv.Ab, tenv.Env, ccdeploy.DeployCCIPContractConfig{ HomeChainSel: tenv.HomeChainSel, FeedChainSel: tenv.FeedChainSel, ChainsToDeploy: tenv.Env.AllChainSelectors(), @@ -45,7 +46,6 @@ func TestInitialDeployOnLocal(t *testing.T) { FeeTokenContracts: tenv.FeeTokenContracts, }) require.NoError(t, err) - require.NoError(t, tenv.Ab.Merge(output.AddressBook)) // Get new state after migration. state, err = ccdeploy.LoadOnchainState(e, tenv.Ab) require.NoError(t, err) diff --git a/integration-tests/web/sdk/internal/schema.graphql b/integration-tests/web/sdk/internal/schema.graphql index 2dabf2c774c..71aa67598da 100644 --- a/integration-tests/web/sdk/internal/schema.graphql +++ b/integration-tests/web/sdk/internal/schema.graphql @@ -182,6 +182,7 @@ type Chain { id: ID! enabled: Boolean! config: String! + network: String! } union ChainPayload = Chain | NotFoundError diff --git a/nix-darwin-shell-hook.sh b/nix-darwin-shell-hook.sh deleted file mode 100755 index 49dbfc60983..00000000000 --- a/nix-darwin-shell-hook.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -# This script is used to set up cross compilation to linux arm64 in a CRIB environment. -# It's loaded during the shell hook execution in shell.nix -main() { - echo "Running in CRIB environment, setting up cross compilation to linux arm64..." - PACKAGE="aarch64-unknown-linux-gnu" - - if ! command -v brew >/dev/null 2>&1; then - echo -e "\e[31mHomebrew is not installed. Please install Homebrew first: https://brew.sh/\e[0m" - exit 1 - fi - - if ! brew list --formula | grep $PACKAGE > /dev/null; then - echo -e "\e[31mThe Homebrew package $PACKAGE is not installed.\e[0m" - echo -e "\e[31mPlease install it by running: brew tap messense/macos-cross-toolchains && brew install ${PACKAGE}\e[0m" - exit 1 - fi - - export GOOS=linux - - installed_version=$(brew list --versions $PACKAGE | awk '{print $2}') - path_prefix=$(brew --prefix) - bin_path=$path_prefix/Cellar/$PACKAGE/$installed_version/bin - - export CC=$bin_path/aarch64-linux-gnu-gcc - export CXX=$bin_path/aarch64-linux-gnu-g++ -} - -main "$@" diff --git a/operator_ui/TAG b/operator_ui/TAG index b085cda408f..e935f83635e 100644 --- a/operator_ui/TAG +++ b/operator_ui/TAG @@ -1 +1 @@ -v0.8.0-b940c8d \ No newline at end of file +v0.8.0-352091e diff --git a/shell.nix b/shell.nix index e3b187dcd96..ba09ebc219d 100644 --- a/shell.nix +++ b/shell.nix @@ -1,4 +1,4 @@ -{pkgs, isCrib}: +{pkgs}: with pkgs; let go = go_1_21; postgresql = postgresql_14; @@ -18,6 +18,7 @@ in nativeBuildInputs = [ go + goreleaser postgresql python3 @@ -50,22 +51,10 @@ in pkg-config libudev-zero libusb1 - ] ++ lib.optionals isCrib [ - nur.repos.goreleaser.goreleaser-pro - patchelf ]; - - shellHook = '' - ${if !isCrib then "" else '' - if [ -z $GORELEASER_KEY ]; then - echo "GORELEASER_KEY must be set in CRIB environments. You can find it in our 1p vault under 'goreleaser-pro-license'." - exit 1 - fi - ${if stdenv.isDarwin then "source ./nix-darwin-shell-hook.sh" else ""} - ''} - ''; - + LD_LIBRARY_PATH = "${stdenv.cc.cc.lib}/lib64:$LD_LIBRARY_PATH"; GOROOT = "${go}/share/go"; + PGDATA = "db"; CL_DATABASE_URL = "postgresql://chainlink:chainlink@localhost:5432/chainlink_test?sslmode=disable"; } diff --git a/tools/bin/goreleaser_utils b/tools/bin/goreleaser_utils index 562dfb96e03..a01c1654133 100755 --- a/tools/bin/goreleaser_utils +++ b/tools/bin/goreleaser_utils @@ -13,38 +13,7 @@ before_hook() { install_local_plugins install_remote_plugins mkdir -p "$lib_path/plugins" - - # Retrieve GOPATH - GOPATH=$(go env GOPATH) - GOARCH=$(go env GOARCH) - - # Define the source directories - BIN_DIR="$GOPATH/bin" - PLUGIN_DIR="$lib_path/plugins" - - # Because we still do cross compilation in the case of - # darwin_arm64 -> linux_arm64, the plugin path will have a suffix of - # linux_arm64, rather than being suffixless on native platforms - if [ "$GOARCH" = "arm64" ]; then - if [ -d "$BIN_DIR/linux_arm64" ]; then - cp "$BIN_DIR/linux_arm64"/chainlink* "$PLUGIN_DIR" - else - cp "$BIN_DIR"/chainlink* "$PLUGIN_DIR" - fi - # Call patchelf --set-interpreter on all plugins - for plugin in "$PLUGIN_DIR"/chainlink*; do - patchelf --set-interpreter /lib/ld-linux-aarch64.so.1 "$plugin" - done - - else - cp "$BIN_DIR"/chainlink* "$PLUGIN_DIR" - - # Call patchelf --set-interpreter on all plugins - for plugin in "$PLUGIN_DIR"/chainlink*; do - patchelf --set-interpreter /lib64/ld-linux-x86_64.so.1 "$plugin" - done - fi - + cp "$(go env GOPATH)"/bin/chainlink* "$lib_path/plugins" } install_local_plugins() { diff --git a/tools/goreleaser-config/gen_config.go b/tools/goreleaser-config/gen_config.go index b0b75b032fa..9108ec1bbd1 100644 --- a/tools/goreleaser-config/gen_config.go +++ b/tools/goreleaser-config/gen_config.go @@ -13,12 +13,10 @@ var validEnvironments = []string{"devspace", "develop", "production"} func Generate(environment string) config.Project { checkEnvironments(environment) - architectures := []string{"amd64", "arm64"} - project := config.Project{ ProjectName: "chainlink", Version: 2, - Env: commonEnv(environment), + Env: commonEnv(), Before: config.Before{ Hooks: []config.Hook{ { @@ -30,7 +28,7 @@ func Generate(environment string) config.Project { }, }, Builds: builds(environment), - Dockers: dockers(environment, architectures), + Dockers: dockers(environment), DockerManifests: dockerManifests(environment), Checksum: config.Checksum{ NameTemplate: "checksums.txt", @@ -56,11 +54,6 @@ func Generate(environment string) config.Project { Disable: "true", }, } - if environment == "devspace" { - versionTemplate := `v0.0.0-{{ .Runtime.Goarch }}-{{ .Now.Format "2006-01-02-15-04-05Z" }}` - project.Snapshot = config.Snapshot{VersionTemplate: versionTemplate} - project.Nightly = config.Nightly{VersionTemplate: versionTemplate} - } // Add SBOMs if needed if environment == "production" { @@ -102,17 +95,12 @@ func checkEnvironments(environment string) { } // commonEnv returns the common environment variables used across environments. -func commonEnv(environment string) []string { - envs := []string{ +func commonEnv() []string { + return []string{ `IMG_PRE={{ if index .Env "IMAGE_PREFIX" }}{{ .Env.IMAGE_PREFIX }}{{ else }}localhost:5001{{ end }}`, `IMG_TAG={{ if index .Env "IMAGE_TAG" }}{{ .Env.IMAGE_TAG }}{{ else }}develop{{ end }}`, - `CGO_ENABLED=1`, - } - - if environment != "devspace" { - envs = append(envs, `VERSION={{ if index .Env "CHAINLINK_VERSION" }}{{ .Env.CHAINLINK_VERSION }}{{ else }}v0.0.0-local{{ end }}`) + `VERSION={{ if index .Env "CHAINLINK_VERSION" }}{{ .Env.CHAINLINK_VERSION }}{{ else }}v0.0.0-local{{ end }}`, } - return envs } // builds returns the build configurations based on the environment. @@ -134,18 +122,13 @@ func builds(environment string) []config.Build { // build creates a build configuration. func build(isDevspace bool) config.Build { - dynamicLinker := `/lib{{ if contains .Runtime.Goarch "amd64" }}64{{end}}/ld-linux-{{ if contains .Runtime.Goarch "arm64" }}aarch64{{ else }}x86-64{{end}}.so.1` - ldflags := []string{ "-s -w -r=$ORIGIN/libs", + "-X github.com/smartcontractkit/chainlink/v2/core/static.Version={{ .Env.VERSION }}", "-X github.com/smartcontractkit/chainlink/v2/core/static.Sha={{ .FullCommit }}", - fmt.Sprintf(`-extldflags "-Wl,--dynamic-linker=%s"`, dynamicLinker), } - if isDevspace { - ldflags = append(ldflags, "-X github.com/smartcontractkit/chainlink/v2/core/static.Version={{ .Version }}") - } else { - ldflags = append(ldflags, "-X github.com/smartcontractkit/chainlink/v2/core/static.Version={{ .Env.VERSION }}") + ldflags[2] = "-X github.com/smartcontractkit/chainlink/v2/core/static.Version={{ .Version }}" } return config.Build{ @@ -165,16 +148,16 @@ func build(isDevspace bool) config.Build { } // dockers returns the docker configurations based on the environment. -func dockers(environment string, architectures []string) []config.Docker { +func dockers(environment string) []config.Docker { var dockers []config.Docker switch environment { case "devspace": dockers = []config.Docker{ docker("linux-amd64", "linux", "amd64", environment, true), - docker("linux-arm64", "linux", "arm64", environment, true), } case "develop", "production": + architectures := []string{"amd64", "arm64"} imageNames := []string{"chainlink", "ccip"} for _, imageName := range imageNames { @@ -230,13 +213,9 @@ func docker(id, goos, goarch, environment string, isDevspace bool) config.Docker `--label=org.opencontainers.image.revision={{ .FullCommit }}`, `--label=org.opencontainers.image.source=https://github.com/smartcontractkit/chainlink`, `--label=org.opencontainers.image.title=chainlink`, + `--label=org.opencontainers.image.version={{ .Env.VERSION }}`, `--label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink`, ) - if !isDevspace { - buildFlagTemplates = append(buildFlagTemplates, - `--label=org.opencontainers.image.version={{ .Env.VERSION }}`, - ) - } dockerConfig := config.Docker{ ID: id, diff --git a/tools/goreleaser-config/main.go b/tools/goreleaser-config/main.go index 1e3e9776b14..852b5b580ae 100644 --- a/tools/goreleaser-config/main.go +++ b/tools/goreleaser-config/main.go @@ -7,7 +7,7 @@ import ( ) func main() { - environments := []string{"develop", "production", "devspace"} + environments := []string{"develop", "production"} for _, e := range environments { cfg := Generate(e) data, err := yaml.Marshal(&cfg)